web-dev-qa-db-ja.com

Facebook React.js:サーバー上でステートフルコンポーネントをどのようにレンダリングしますか?

React.jsを使用したサーバー側レンダリングで概念的に何かが足りないと思います

サーバー側のDBからのアイテムを表示するページを作成し、それらをフィルターするための入力フィールドがあるとします。

ページが欲しい:

  • /items?name=foobarなどのURLに応答する
  • React入力フィールドを使用して、アイテムを名前でフィルタリングします
  • Reactコンポーネントを使用して、フィルタリングされたアイテムのリストを表示します

パブリックREST APIを使用して、クライアント側のアイテムをクエリするとします。

概念的には、最初のリクエスト(GET /items?name=foobar)でやりたいことは次のとおりです。

  • 入力フィールドにユーザーがパラメーターとして渡したものを表示したいので、クエリパラメーター( 'foobar')を 'prop'(initialNameなど)としてreactコンポーネントに渡す必要があります

だから私はこれを試しました:

 // 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
                })
            ]
        });
    }
});
  • また、データベースクエリサーバー上を実行してアイテムのリストを取得し、このアイテムのリストをItemListの 'prop'( '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コンポーネントに渡す最初の小道具は何ですか?

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コンポーネントのマークアップだけなので、これは私のコンポーネントの実装の問題だと思いますが、どこから始めればよいのか本当にわかりません。

私は正しい軌道に乗っていますか?または、何かを完全に見逃していますか?

29
phtrivier

サーバーレンダリングを使用する場合は、サーバーでコンポーネントをレンダリングするために使用したのと同じプロップを常に渡す必要があります。この場合、React.renderComponentがサーバーでレンダリングされたマークアップを取得するために、同じinitialItemsプロップを渡す必要があります(プロップをJSON化してrenderComponentへの呼び出しに含めることにより)。

指定された場合のinitialItemsからの読み取りの一般的な構造は、私には理にかなっています。そうすることで、データがプリロードされたコンポーネントまたはデータがないコンポーネントを作成できます。新しい状態を最初からレンダリングする場合に何を表示するかによって、初期状態を設定する必要があります。 (サーバーでレンダリングされたマークアップを常に取得している場合は、initialNameおよびinitialItemsプロパティを常に渡すだけです。)

それが理にかなっていると思います。

14
Sophie Alpert