したがって、これはかなり自由回答の質問であり、これを実装する可能性はほとんど無限です...しかし、とにかく試してみます。この答えは教義ではなく、それを行うための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
関数をTh
usingに渡します
<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
を確実にディスパッチすることです。tableId
onClick*