Подтвердить что ты не робот

Facebook React.js: как вы отображаете компоненты с состоянием на сервере?

Я думаю, что концептуально чего-то не хватает при рендеринге на стороне сервера с использованием React.js

Предположим, я хочу создать страницу для отображения элементов из серверной БД с полем ввода для их фильтрации.

Я хочу страницу:

  • который отвечает на URL, как /items?name=foobar
  • с полем ввода React для фильтрации элементов по имени
  • с компонентом React для отображения списка отфильтрованных элементов

Предположим, у меня есть открытый REST API для запроса элементов на стороне клиента.

Концептуально, что я хочу сделать при первом запросе (GET/items?name=foobar):

  • Я хочу, чтобы мое поле ввода показывало, что пользователь передал в качестве параметра, поэтому мне нужно передать параметр запроса ('foobar') компоненту реагирования в виде 'prop' (например, 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
                })
            ]
        });
    }
});
  • Я также должен выполнить запрос к базе данных на сервере, чтобы получить список элементов, и передать этот список элементов как "prop" ItemList (например, "initialItems")

Насколько я понимаю, мне нужен простой компонент для отображения списка, получая его как "prop":

 // 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 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.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, поэтому я предполагаю, что это проблема реализации моего компонента, но я не знаю, с чего начать.

Я на правильном пути? Или я что-то упускаю полностью?

4b9b3361

Ответ 1

При использовании рендеринга сервера вы всегда должны передавать те же реквизиты, которые использовались для рендеринга компонента на сервере. В этом случае вам нужно передать ту же самую начальную идентификационную строку, чтобы React.renderComponent подобрал разметку, обработанную сервером (просто JSONifying реквизита и поместив ее в вызов renderComponent).

Ваша общая структура чтения из initialItems, когда это указано, имеет смысл для меня. Это позволяет вам либо составлять компонент с предварительно загруженными данными, либо тот, у которого его нет. То, что вам нужно, чтобы установить начальное состояние, зависит от того, что вы хотите показать в тех случаях, когда вы создаете совершенно новый компонент с нуля. (Если вы всегда набираете разметку, обработанную сервером, вы можете просто всегда передавать реквизиты initialName и initialItems.)

Надеюсь, что это имеет смысл.