Написание HoC, который обменивается данными с redux / redux-saga - PullRequest
0 голосов
/ 29 ноября 2018

В настоящее время я пытаюсь создать повторно используемый компонент, который будет извлекать данные из удаленного источника и обновлять таблицу по мере необходимости, если элементы будут меняться на серверной части.Вот простой компонент:

<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 /> добавил эту строку в верхнюю часть списка. Проблема в том, что мне нужно было бы подключить дополнительную логику к имеющейся у меня избыточной саге. Сначала я думал, чтодобавьте реквизит в DataTable, например deletedItem={DELETE_ORDER_SUCCESS}, но это выглядит как очень неприятный обходной путь. Моему редуктору нужно было бы перебрать каждую визуализированную таблицу в срезе и проверить, было ли инициализировано action.type === той, которая была инициализированакогда отображается таблица.

В настоящее время я использую React Boilerplate .

TLDR:

  • Наличие многократно используемого компонента DataTable
  • Требуется получить данные / заполнить таблицу.Имеет функцию поиска / ограничения / разбиения на страницы
  • Хотите добавить дополнительные хуки (например, addItem), которые добавят элемент в верхнюю часть списка, если он получен из веб-сокета

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

Я бы настроил 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 res на let res

затем на компоненте unmount вы должны отправить действие, чтобы разрешить закрытие веб-сокета.(вы можете сохранить карту открытых веб-сокетов с идентификатором)

0 голосов
/ 04 декабря 2018

У вас может быть набор редукторов для действий, связанных с веб-сокетами.Допустим, когда новый заказ получен через веб-сокеты, вы можете отправить действие NEW_ORDER.

В ваших редукторах вы можете обновить существующее состояние, добавив новый заказ (в верхней части списка, если эточто вам нужно).Это должно вызвать рендеринг, так как данные таблицы читаются из хранилища Redux.

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

Вы можете выполнять разные действия для разных вещей, которые вам нужно выполнить.

...