また、問題をよりよく表現してください。変換が必要な不変のデータを持つ純粋な汎用のReactコンポーネントの作成方法
react/reduxアーキテクチャでは、多くの例が見つからないという問題が発生しました。
私のアプリケーションは、パフォーマンスのために不変性、参照平等およびPureRenderMixin
に依存しています。しかし、このようなアーキテクチャでは、より汎用的で再利用可能なコンポーネントを作成することは難しいです。
const ListView = React.createClass({
mixins: [PureRenderMixin],
propTypes: {
items: arrayOf(shape({
icon: string.isRequired,
title: string.isRequired,
description: string.isRequired,
})).isRequired,
},
render() {
return (
<ul>
{this.props.items.map((item, idx) => (
<li key={idx}>
<img src={item.icon} />
<h3>{item.title}</h3>
<p>{item.description}</p>
</li>
)}
</ul>
);
},
});
そして私はReduxのストアからユーザーを表示するには、リストビューを使用したい:
は、私はこのような一般的なListView
を考えてみましょう。彼らはこの構造を持っています:
const usersList = [
{ name: 'John Appleseed', age: 18, avatar: 'generic-user.png' },
{ name: 'Kate Example', age: 32, avatar: 'kate.png' },
];
これはこれまで私のアプリケーションでは非常に正常なシナリオです。私は通常、それが好きなレンダリング:
<ListView items={usersList.map(user => ({
title: user.name,
icon: user.avatar,
description: `Age: ${user.age}`,
}))} />
問題はmap
が基準平等を壊すということです。 usersList.map(...)
の結果は常に新しいリストオブジェクトになります。したがって、usersList
が以前のレンダリングから変更されていない場合でも、ListView
はまだそれがitems
-listと等しいことを知ることができません。
私はこれに3つの解決策を見ることができますが、私はそれらがとても好きではありません。
解決策1:パラメータとしてtransformItem
-functionをパスし、items
これは非常にうまくいく生のオブジェクトの配列
const ListView = React.createClass({
mixins: [PureRenderMixin],
propTypes: {
items: arrayOf(object).isRequired,
transformItem: func,
},
getDefaultProps() {
return {
transformItem: item => item,
}
},
render() {
return (
<ul>
{this.props.items.map((item, idx) => {
const transformedItem = this.props.transformItem(item);
return (
<li key={idx}>
<img src={transformedItem.icon} />
<h3>{transformedItem.title}</h3>
<p>{transformedItem.description}</p>
</li>
);
})}
</ul>
);
},
});
とします。
<ListView
items={usersList}
transformItem={user => ({
title: user.name,
icon: user.avatar,
description: `Age: ${user.age}`,
})}
/>
そしてusersList
変更ListViewコントロールのみ再レンダリングされます:私は、このように私のユーザーのリストをレンダリングすることができます。しかし、私はその途中で小道具タイプの検証を失った。
解決方法2:
const UsersList = React.createClass({
mixins: [PureRenderMixin],
propTypes: {
items: arrayOf(shape({
name: string.isRequired,
avatar: string.isRequired,
age: number.isRequired,
})),
},
render() {
return (
<ListView
items={this.props.items.map(user => ({
title: user.name,
icon: user.avatar,
description: user.age,
}))}
/>
);
}
});
しかし、それはより多くのコードです:ListView
をラップ専門の部品を作ろう!私はステートレスコンポーネントを使用することができれば良いでしょうが、PureRenderMixin
の通常のコンポーネントのように動作するかどうかはわかりません。 (ステートレスコンポーネントはまだ悲しいことに反応するのドキュメント化されていない機能です)
const UsersList = ({ users }) => (
<ListView
items={users.map(user => ({
title: user.name,
icon: user.avatar,
description: user.age,
}))}
/>
);
解決策3:一般的な純粋な変換コンポーネントを作成します
const ParameterTransformer = React.createClass({
mixin: [PureRenderMixin],
propTypes: {
component: object.isRequired,
transformers: arrayOf(func).isRequired,
},
render() {
let transformed = {};
this.props.transformers.forEach((transformer, propName) => {
transformed[propName] = transformer(this.props[propName]);
});
return (
<this.props.component
{...this.props}
{...transformed}
>
{this.props.children}
</this.props.component>
);
}
});
と
<ParameterTransformer
component={ListView}
items={usersList}
transformers={{
items: user => ({
title: user.name,
icon: user.avatar,
description: `Age: ${user.age}`,
})
}}
/>
がありますようにそれを使用しますこれらの唯一の解決策、または私は何かを逃したか?
私が現在取り組んでいるコンポーネントには、これらのソリューションはどれも適合しません。ナンバーワンが最適ですが、それが私が続けることを選択したトラックのように感じると、私が作る汎用コンポーネントのすべての種類は、これらのtransform
プロパティを持つ必要があります。また、そのようなコンポーネントを作成するときにReact propタイプの検証を使用することができないという大きな欠点もあります。
解決策2は、おそらく最も意味の訂正であり、少なくともこれはListView
の例です。しかし、私はそれが非常に冗長であると思うし、また、私の実際のユースケースにはうまく適合しません。
解決策3はあまりにも魔法であり、判読できません。
メモ化、はい、もちろん!どのように私はそれについて考えることができない? アイデアはストローク・ミーですが、それまで考えていなかったので、これまでに選択したすべてのリストの大きなキャッシュになると思いました。しかし、ソースの、最新の結果よりも多くを保持する必要はありませんし、それに対してチェックしてください。 ライブラリを引っ張る代わりに、自分で実装するほうがおそらく簡単です。 –
確かに、自分で実装することができます - 再選択は[コードの82行だけです](https://github.com/rackt/reselect/blob/master/src/index.js#L82)です。 –