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

28

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

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

Мне нужна страница:

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

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

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

  • Я хочу, чтобы мое поле ввода отображало, что пользователь передал как параметр, поэтому мне нужно передайте параметр запроса ( "foobar" ) на компонент реакции, как "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 (например, "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 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 на стороне клиента, что мне делать? Я знаю, где я хочу, чтобы мой дом был визуализирован, но какой исходный реквизит следует передать компоненту реакции?

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 "удаляется" на стороне клиента, с этим сообщением:

Реакция попыталась использовать разметку повторного использования в контейнере, но контрольная сумма недействительно. Обычно это означает, что вы используете рендеринг сервера и разметка, сгенерированная на сервере, была не тем, что клиент ожидая. Реагируйте новую инъекцию, чтобы компенсировать, какие работы, но вы потеряли многие преимущества рендеринга сервера. Вместо этого, цифра почему создается разметка на клиенте, сервер.

Обратите внимание, что только разметка моего компонента ItemList удаляется, поэтому я предполагаю, что это проблема с реализацией моего компонента, но я не знаю, с чего начать.

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

Спасибо

Теги:
isomorphic-javascript

1 ответ

14
Лучший ответ

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

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

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

  • 1
    Означает ли это, что я также должен всегда делать запрос на стороне клиента, чтобы получить список initialItems, чтобы я мог передать их как initialItems на стороне клиента? Я думал о том, чтобы на самом деле встроить json в сгенерированную на стороне сервера страницу, чтобы клиентская сторона могла начать с чего-то ... потому что я не хочу, чтобы моя клиентская сторона выполняла тот же запрос API, что не соответствовало бы цели рендеринг на стороне сервера в моем случае ...
  • 7
    Извините, если мне было непонятно - мое предложение было включить JSON в сгенерированную сервером страницу.
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню