エンティティが同じタイプの子を持つことができるレデューサーを実装する方法について、私は少し行き詰まっています。
例としてredditコメントを考えてみましょう。各コメントには、コメント自体を持つことができる子コメントを含めることができます。簡略化のため、コメントは{id, pageId, value, children}
タイプのレコードであり、pageId
がredditページです。
その周りの減速機をどのようにモデル化しますか? pageId
を使用してページごとにフィルターできるコメントのID->マップをリデューサーにすることを考えていました。
問題は、たとえば、ネストされたコメントにコメントを追加する場合です。マップのルートにレコードを作成し、そのIDを親の子プロパティに追加する必要があります。すべてを取得するために必要なすべてのコメントを表示するには、上部にあるコメント(たとえば、orderedListとしてページリデューサーに保持されるもの)をフィルター処理してから、それらを繰り返し、次の場合にコメントオブジェクトからフェッチします。再帰を使って子供たちに出会います。
それより良いアプローチはありますか、それとも欠陥がありますか?
これに対する公式の解決策は、 normalizr を使用して、次のように状態を維持することです。
_{
comments: {
1: {
id: 1,
children: [2, 3]
},
2: {
id: 2,
children: []
},
3: {
id: 3,
children: [42]
},
...
}
}
_
Comment
コンポーネントをconnect()
する必要があるのは正しいことです。これにより、各コンポーネントがReduxストアから関心のあるchildren
を再帰的にクエリできるようになります。
_class Comment extends Component {
static propTypes = {
comment: PropTypes.object.isRequired,
childComments: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired
},
render() {
return (
<div>
{this.props.comment.text}
{this.props.childComments.map(child => <Comment key={child.id} comment={child} />)}
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {
childComments: ownProps.comment.children.map(id => state.comments[id])
};
}
Comment = connect(mapStateToProps)(Comment);
export default Comment;
_
これは良い妥協案だと思います。 comment
をプロップとして渡しますが、コンポーネントはストアからchildrenComments
を取得します。
ストア(レデューサー)の構造は、目的のビューモデル(小道具としてコンポーネントに渡すモデル)と異なる場合があります。すべてのコメントを配列に保持し、高レベルの「スマート」コンポーネントのmapStateToPropsにあるリンクによってそれらをツリーにマップすることができます。レデューサーでの簡単な状態管理と、コンポーネントが動作する便利なビューモデルが得られます。