Facebook React.js:如何在服务器上呈现有状态组件?

时间:2014-01-07 18:21:07

标签: reactjs isomorphic-javascript

我认为我在概念上缺少使用React.js的服务器端渲染的东西

假设我想创建一个页面来显示来自服务器端数据库的项目,并带有输入字段 过滤它们。

我想要一个页面:

  • 响应/items?name=foobar
  • 之类的网址
  • 使用React输入字段按名称过滤项目
  • 使用React组件显示已过滤的项目列表

假设我有一个公共REST API来查询客户端的项目。

从概念上讲,我在第一次请求(GET /items?name=foobar)时想要做的是:

  • 我希望我的输入字段显示用户作为参数传递的内容,所以我需要 将查询参数('foobar')传递给react组件,作为'prop'(如initialName

所以我尝试了这个:

 // A statefull 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
                })
            ]
        });
    }
});
  • 我还必须在服务器上进行数据库查询以获取项目列表,并且 将此项列表作为ItemList的'prop'传递(如'initialItems')

据我了解,我需要一个简单的组件来显示列表,将其作为“道具”接收:

 // 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(" ")
                        })]
                });
            })
        });

    }
});
  • 现在,我需要一个组件来显示整个页面;此组件必须维护整个页面的状态,即查看字段中输入的名称,并进行API查询以更新列表项。 但是我不明白这个组件如何具有'initialState',它既可以在服务器端工作,也可以在客户端进行后续渲染。

我试过了:

 // A statefull 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' propery 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.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尝试在容器中使用重用标记但校验和   无效。这通常意味着您正在使用服务器呈现   并且在服务器上生成的标记不是客户端的标记   期待。反应注入新的标记,以补偿哪些有效,但你   失去了服务器渲染的许多好处。相反,图   为什么生成的标记在客户端上是不同的   服务器

请注意,只删除了我的ItemList组件的标记,所以我认为这是我的组件的实现问题,但我真的不知道从哪里开始。

我是否在正确的轨道上?或者我完全错过了什么?

由于

1 个答案:

答案 0 :(得分:14)

使用服务器渲染时,应始终传递用于在服务器上呈现组件的相同道具。在这种情况下,您需要传递相同的initialItems prop,以便React.renderComponent获取服务器呈现的标记(通过简单地JSONifying道具并将其置于renderComponent的调用中)。 / p>

指定时从initialItems读取的一般结构对我有意义。这样做允许您创建具有预加载数据的组件或不具有预加载数据的组件。您需要将初始状态设置为取决于您要从头开始渲染全新组件时要显示的内容。 (如果你总是拿起服务器渲染的标记,那么你可以简单地传递initialNameinitialItems道具。)

希望这是有道理的。