我认为我在概念上使用React.js在服务器端渲染中缺少一些东西
假设我想创建一个页面来显示服务器端数据库中的项目,并使用输入字段对其进行过滤。
我想要一个页面:
/items?name=foobar
假设我有一个公共的REST API来查询客户端上的项目。
从概念上讲,我首先要执行的操作(GET /items?name=foobar)是:
GET /items?name=foobar
initialName
所以我尝试了这个:
// A stateful component, maintaining the value of a query field var ItemForm = React.createClass({ getInitialState : function () { return { name : this.props.initialName }; }, handleInputValueChanged : function() { var enteredName = this.refs.query.getDOMNode().value.trim(); this.props.onNameChanged(enteredName); }, render : function () { return React.DOM.form({ children : [ React.DOM.label({ children : "System name" }), React.DOM.input({ ref : "query", value : this.state.name, onChange : this.handleInputValueChanged }) ] }); } });
据我了解,我需要一个简单的组件来显示列表,并将其作为“属性”接收:
// A stateless component, displaying a list of item var ItemList = return React.createClass({ propTypes : { items : React.PropTypes.array }, render : function () { return React.DOM.ul({ children : _.map(this.props.items, function (item) { return React.DOM.li({ children : [ React.DOM.span({ children : ["Name : ", item.name].join(" ") })] }); }) }); } });
我尝试了这个:
// A stateful react component, maintaining the list of items var ItemPage = React.createClass({ getInitialState : function () { // ????? // This is where I'm sure the problem lies. // How can this be known both on server and client side ? return { items : this.props.initialItems || [] }; }, queryItems : function (enteredName) { var self = this; // The field was emptied, we must clear everything if (!enteredName) { this.setState({ items : [] }); } else { // The field was changed, we want to do a query // then change the state to trigger a UI update. // The query code is irrelevant, I think. doQuery(enteredName).then(function (items) { self.setState({ items : items }); }); } }, render : function () { // I don't want to display the same view // if there is no results. // This uses a 'state' property of the page var results = null; if (_.isEmpty(this.state.items)) { results = React.DOM.div({ ref : "results", children : "No results" }); } else { results = ItemListView({ // Here items is a 'prop', the ItemList is technically // stateless items : this.state.items }); } return React.DOM.div({ children : [ ItemForm({ initialName : this.props.initialName, onNameChanged : this.queryItems }), results ] }); } });
那就是我被困住的地方。我可以使用以下方式在服务器端渲染内容:
var name = // The name from the query parameters var items = doQueryOnServerSide(name); React.renderComponentAsString(ItemPage({ initialName : name, initialItems : items });
但是,当我尝试编写客户端javascript时,该怎么办?我知道要在哪里渲染我的dom,但是我应该将哪些初始道具传递给react组件?
React.renderComponent(ItemPage({ initialName : ??? // Read the name parameter from URL ? initialItems : ??? // I don't know yet, and I don't want to make an API call until the user entered something in the input box });
我尝试过的大多数尝试最终都会在客户端上“擦除” DOM,并显示以下消息:
React尝试在容器中使用重用标记,但是校验和无效。通常,这意味着您正在使用服务器呈现,并且服务器上生成的标记不是客户端期望的。React注入了新的标记来补偿可以正常工作的标记,但是您已经失去了服务器渲染的许多好处。相反,要弄清为什么生成的标记在客户端或服务器上不同。
请注意,仅擦除了ItemList组件的标记,因此我认为这是该组件的实现问题,但我真的不知道从哪里开始。
我在正确的轨道上吗?还是我完全想念东西?
使用服务器渲染时,应始终传递与用于在服务器上渲染组件相同的道具。在这种情况下,您需要传递相同的initialItems道具,以获取React.renderComponent服务器渲染的标记(通过简单地对道具进行JSON化并将其放入调用中renderComponent)。
React.renderComponent
renderComponent
当指定时,您从initialItems读取的一般结构对我来说很有意义。这样做使您可以使用预加载的数据或不使用预加载的数据来制作组件。设置初始状态所需的条件取决于在从头开始渲染全新组件的情况下要显示的内容。(如果您始终使用服务器渲染的标记,则只需传递initialName和即可initialItems。)
initialItems
希望有道理。