繁体   English   中英

编写一个与redux / redux-saga共享数据的HoC

[英]Writing a HoC that shares data with redux/redux-saga

我目前正在尝试创建一个可重用的组件,它将从远程源中提取数据,并在后端更改项目时根据需要更新表。 这是一个简单的组件:

<React.Fragment>
    <Header />
    <table>
      <thead>
        <tr>
          {columns.map(c => (
            <th key={c.header}>{c.header}</th>
          ))}
        </tr>
      </thead>
      <tbody>{data.map(row => children(row))}</tbody>
    </table>
  <Pagination />
</React.Fragment>

目标是,从我的订单页面,我可以像下面这样称呼它:

<DataTable
  id="app/OrderPage/orders"
  url="http://localhost:8081/api/orders"
  columns={[
    { header: 'Name' },
    { header: 'Actions' },
  ]}
>
  {order => (
      <tr key={order.id}>
        <td>{order.name}</td>
        <td>
          <Link to={`/admin/orders/${order.id}`}>
            View
          </Link>
        </td>
      </tr>
    )}
</DataTable>

现在,我的想法是想在多个地方重用这个组件。 我目前使用redux-saga来进行提取,并在返回数据时更新数据表。 (目前我正在提供一个数据表ID,然后传递给FETCH_DATA,LIMIT_CHANGED,SEARCH_CHANGED等等,然后我用它来更新需要更改的切片部分:

function dataTableReducer(state = initialState, action) {
  switch (action.type) {
    case INIT: {
      const { id, url, deletedItem } = action;
      return state.setIn(
        ['tables', id],
        initialTableState
          .set('id', id)
          .set('url', url)
          .set('deletedItem', deletedItem),
      );
    }
    case LOAD_DATA_SUCCESS:
      return state.setIn(['tables', action.id, 'data'], action.data.data);
    case LIMIT_CHANGED:
      return state.setIn(['tables', action.id, 'limit'], action.limit);
    case SEARCH_CHANGED:
      return state.setIn(['tables', action.id, 'search'], action.search);
    default:
      return state;
  }
}

我目前的DataTable组件的传奇:

function* getData({ id }) {
  const dt = yield select(selectDataTableInstance(), { id });
  const { url, limit, search, page } = dt;

  const res = yield call(request, url, {
    qs: {
      limit,
      search,
      page,
    },
  });

  yield put(loadDataSuccess(id, res));
}

// Individual exports for testing
export default function* dataTableSaga() {
  yield all([
    takeEvery(LOAD_DATA, getData),
    takeEvery(LIMIT_CHANGED, getData),
    takeLatest(SEARCH_CHANGED, getData),
  ]);
}

现在的问题是, 如何向该组件添加其他逻辑 例如,当一个新订单通过websocket进入时,我希望<DataTable />组件将该行添加到列表顶部。 问题是,我需要将额外的逻辑连接到我现有的redux-saga。 我的想法最初是为DataTable添加一个prop,比如deletedItem={DELETE_ORDER_SUCCESS} ,但这似乎是一个非常讨厌的解决方法。 我的reducer需要遍历切片中的每个渲染表,并检查action.type ===是否是在显示表时初始化的那个。

我目前正在使用React Boilerplate

TLDR:

  • 拥有可重用的DataTable组件
  • 需要获取数据/填充表。 具有搜索/限制/分页功能
  • 想要添加额外的钩子(比如addItem),这会将项目添加到列表的顶部,如果一个来自websocket

您可以为Web套接字相关操作设置一组Reducer。 假设在Web套接字上收到新订单时,您可以发送NEW_ORDER操作。

在reducers中,您可以更新现有状态以包含新订单(如果您需要,则在列表顶部)。 这应该触发重新渲染,因为正在从Redux存储中读取表数据。

case LOAD_DATA_SUCCESS:
  return state.setIn(['tables', action.id, 'data'], [action.data.data, state.getIn(['tables', 'action.id', 'data']);

您可以针对需要执行的不同操作执行不同的操作。

我会在yield put(loadDataSuccess(id, res));之后在你的saga中设置websocket yield put(loadDataSuccess(id, res)); 并添加这样的处理程序:

var ws = new WebSocket(wsAddress);
ws.onmessage = function (event) {
 const newItem = event.data;
 res = {...res, data:[newItem ,...res.data]};
 yield put(loadDataSuccess(id, res));
}

你需要改变你的const reslet res

然后在组件卸载时,您应该发送一个动作以允许关闭websocket。 (你可以保留一个由id键入的开放式websockets的地图)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM