15

サーバー主導のデータが複数のページのテーブルに表示されています。

現在、次のテーブルアクションがあります

pageChange
pageSizeChange
sort
load
loaded

ロードをトリガーする必要があるいくつかのページにフィルターがあります。

この機能を使用する複数のエンティティがあり、上記のロジックのほとんどを共有しますが、個別のロード機能の定義が必要になります。

私が考えたのは、テーブル ID をパラメーターとして受け取るアクションを作成してから、この ID を受け取り、エンティティ内にテーブル ノードをマウントする createTableReducer 関数を作成することでした

redux saga のようなものを使用せずに、一般的なアクションからエンティティ固有のロード アクションをトリガーするにはどうすればよいですか?

高次のコンポーネントを作成してロード関数に渡すことについて疑問に思っていましたが、これで問題が解決するとは思いません。テーブル コンポーネント自体から change アクションと load アクションの両方を呼び出すこともできますが、これは良い解決策とは思えません。

4

1 に答える 1

20

したがって、これはかなり自由回答の質問であり、これを実装する可能性はほとんど無限です...しかし、とにかく試してみます。この答えは教義ではなく、それを行うための1 つの可能な方法にすぎません。

好きな場所にテーブルを配置できることを願うことから始めましょう。あるテーブルを別のテーブルと区別するには、ストア内の対応するデータにtableIdリンクするプロパティを渡すだけです。Table

<Table tableId='customers' />
<Table tableId='products' />
<Table tableId='orders' />
<Table tableId='transactions' />

次に、操作する状態シェイプを定義しましょう。各最上位キーは、tableIdデータを保存する対象と一致します。この形は好きなように作ることができますが、私がこれを提供しているので、後のコードで参照するときに頭の中で視覚化する必要が少なくなります。tableId一貫して参照する限り、最上位のキーに好きな名前を付けることもできます。

{
   customers: {
     page: 0,
     sortBy: 'id',
     cols: [
       {id: 'id',         name: 'name'},
       {id: 'username',   name: 'Username'},
       {id: 'first_name', name: 'First Name'},
       {id: 'last_name',  name: 'Last Name'},
       ...
     ],
     rows: [
       {id: 1, username: 'bob', first_name: 'Bob', last_name: 'Smith', ...},
       ...
     ]
  },
  products: {
    ...
  },
  orders: {
    ...
  }
  ...
}

さて、厄介な部分であるTableコンテナを片付けましょう。一度に消化するのは大変ですが、重要な部分を個別に分解しますのでご安心ください。

コンテナー/Table.js

import React from 'react';
import {connect} from 'react-redux';
import actions as * from './actions/';

const _Table = ({cols, rows, onClickSort, onClickNextPage}) => (
  <div>
    <table>
      <thead>
        <tr>
         {cols.map((col,key) => (
           <Th key={key} onClickSort={onClickSort(col.id)}>{col.name}</Th>
         )}
        </tr>
      </thead>
      <tbody>
        {rows.map((row,key) => ...}
      </tbody>
    </table>
    <button onClick={onClickNextPage}>Next Page</button>
  </div>
);

const Table = connect(
  ({table}, {tableId}) => ({    // example: if tableId = "customers" ...
    cols: table[tableId].cols,  // state.table.customers.cols
    rows: table[tableId].rows   // state.table.customers.rows
  }),
  (dispatch, {tableId}) => ({
    onClickSort: columnId => event => {
      dispatch(actions.tableSortColumn(tableId, columnId));
      // example: if user clicks 'last_name' column in customers table
      // dispatch(actions.tableSortColumn('customers', 'last_name'));
    },
    onClickNextPage: event => {
      dispatch(actions.tableNextPage(tableId))
    }
  })
)(_Table);

export default Table;

この投稿から 1 つだけ学ぶことがあるとすれば、それは次のとおりです。

ここで注意すべきことは、mapStateToPropsと という 2 番目の引数を受け入れるmapDispatchToProps ことownPropsです。

// did you know you can pass a second arg to these functions ?
const MyContainer = connect({
  (state, ownProps) => ...
  (dispatch, ownProps) => ...
})(...);

上で書いたコンテナで、関心のある各 vars を分解しています。

// Remember how I used the container ?
// here ownProps = {tableId: "customers"}
<Table tableId="customers" />

今私がどのように使用したかを見てくださいconnect

const Table = connect(
  // mapStateToProps
  ({table}, {tableId}) => ...
    // table = state.table
    // tableId = ownProps.tableId = "customers"


  // mapDispatchToProps
  (dispatch, {tableId}) => ...
    // dispatch = dispatch
    // tableId = ownProps.tableId = "customers"
)(_Table);

_Tableこのようにして、基になるコンポーネント ( )のディスパッチ ハンドラーを作成するときにtableId、ハンドラー内で使用できるようになります。_Tableコンポーネント自体は、tableId必要がなければプロップに関係する必要さえないというのは、実際にはちょっといいことです。

次に、onClickSort関数の定義方法に注目してください。

onClickSort: columnId => event => {
  dispatch(actions.tableSortColumn(tableId, columnId));
}

コンポーネントはこの_Table関数をThusingに渡します

<Th key={key} onClickSort={onClickSort(col.id)}>{col.name}</Th>

ここでハンドラーにのみ送信する方法を参照してください。columnId次に、最終的にアクションをディスパッチするThを送信する方法を確認します。event

コンポーネント/テーブル/Th.js

import React from 'react';

const Th = ({onClickSort, children}) => (
  <th>
    <a href="#sort" onClickSort={event => {
      event.preventDefault();
      onClickSort(event);
    }}>{children}</a>
  </th>
);

export default Th;

不要な場合はそのままにしておく必要eventはありませんが、何かに活用したい場合に備えて、取り付け方をご紹介したいと思います。

移動中...

アクション/index.js

アクション ファイルはかなり標準的なものになります。tableId各テーブル アクションでアクセスできることに注意してください。

export const TABLE_SORT_COLUMN = 'TABLE_SORT_COLUMN';
export const TABLE_NEXT_PAGE = 'TABLE_NEXT_PAGE';

export const tableSortColumn = (tableId, columnId) => ({
  type: TABLE_SORT_COLUMN, payload: {tableId, columnId}
});

export const tableNextPage = (tableId) => ({
  type: TABLE_NEXT_PAGE, payload: {tableId}
});

...

最後に、あなたtableReducerはこのように見えるかもしれません。繰り返しますが、ここでも特別なことは何もありません。アクション タイプに対して定期的に実行switchし、それに応じて状態を更新します。を使用して、状態の適切な部分に影響を与えることができますaction.payload.tableId。私が提案した状態の形状を覚えておいてください。別の状態形状を選択した場合は、このコードを変更して一致させる必要があります

const defaultState = {
  customers: {
    page: 0,
    sortBy: null,
    cols: [],
    rows: []
  }
};

// deep object assignment for nested objects
const tableAssign = (state, tableId, data) =>
  Object.assign({}, state, {
    [tableId]: Object.assign({}, state[tableId], data)
  });

const tableReducer = (state=defaultState, {type, payload}) => {
  switch (type) {
    case TABLE_SORT_COLUMN:
      return tableAssign(state, payload.tableId, {
        sortBy: payload.columnId
      });
    case TABLE_NEXT_PAGE:
      return tableAssign(state, payload.tableId, {
        page: state[payload.tableId].page + 1
      });
    default:
      return state;
  }
};

備考:

テーブル データの非同期読み込みについては説明しません。このようなワークフローは、redux docs: Async Actionsですでに詳しく説明されています。redux-thunk、redux-promise、または redux-saga の選択はあなた次第です。よくわかる方を選んでください!適切に実装するための鍵は、他のハンドラーで行ったように、(必要な他のパラメーターと共に)TABLE_DATA_FETCHを確実にディスパッチすることです。tableIdonClick*

于 2016-03-09T16:16:15.993 に答える