web-dev-qa-db-ja.com

Reduxレデューサーでツリー型エンティティを処理する方法は?

エンティティが同じタイプの子を持つことができるレデューサーを実装する方法について、私は少し行き詰まっています。

例としてredditコメントを考えてみましょう。各コメントには、コメント自体を持つことができる子コメントを含めることができます。簡略化のため、コメントは{id, pageId, value, children}タイプのレコードであり、pageIdがredditページです。

その周りの減速機をどのようにモデル化しますか? pageIdを使用してページごとにフィルターできるコメントのID->マップをリデューサーにすることを考えていました。

問題は、たとえば、ネストされたコメントにコメントを追加する場合です。マップのルートにレコードを作成し、そのIDを親の子プロパティに追加する必要があります。すべてを取得するために必要なすべてのコメントを表示するには、上部にあるコメント(たとえば、orderedListとしてページリデューサーに保持されるもの)をフィルター処理してから、それらを繰り返し、次の場合にコメントオブジェクトからフェッチします。再帰を使って子供たちに出会います。

それより良いアプローチはありますか、それとも欠陥がありますか?

22
Vincent P

これに対する公式の解決策は、 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を取得します。

36
Dan Abramov

ストア(レデューサー)の構造は、目的のビューモデル(小道具としてコンポーネントに渡すモデル)と異なる場合があります。すべてのコメントを配列に保持し、高レベルの「スマート」コンポーネントのmapStateToPropsにあるリンクによってそれらをツリーにマップすることができます。レデューサーでの簡単な状態管理と、コンポーネントが動作する便利なビューモデルが得られます。

1
Victor Suzdalev