React.jsを使用したサーバー側レンダリングで概念的に何かが欠けていると思います
サーバー側の DB からアイテムを表示するページを作成し、それらをフィルタリングするための入力フィールドを使用するとします。
ページが欲しい:
- のような 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
})
]
});
}
});
- また、アイテムのリストを取得するためにサーバーでデータベース クエリを実行し、このアイテムのリストを ItemList の「小道具」として渡す必要があります (「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(" ")
})]
});
})
});
}
});
- ここで、 Page 全体を表示するコンポーネントが必要です。このコンポーネントは、ページ全体の状態を維持する必要があります。つまり、フィールドに入力された名前を確認し、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 コンポーネントのマークアップだけが消去されていることに注意してください。これは私のコンポーネントの実装の問題だと思いますが、どこから始めればよいかわかりません。
私は正しい軌道に乗っていますか?または、何かを完全に見逃していますか?