Можно ли создать заводскую функцию React Hook? - PullRequest
2 голосов
/ 21 июня 2019

У меня есть приложение React, которое сильно зависит от redux, react-redux и redux-saga.Я начинаю экспериментировать с React Hooks и хуками useSelector и useDispatch в react-redux, и я столкнулся с вопросом, как по существу написать фабричную функцию для генерации хуков для каждого из моих узлов-избыточников..

В общем, у меня есть редукционный узел для каждой конечной точки API, которую использует мое приложение.В этом приложении есть около 100 уникальных конечных точек, а значит, около 100 узлов-резервистов.Каждый из этих узлов затем соответствует одному state-[node].js файлу, например state-users.js и т. Д. Каждый из этих файлов состояния инкапсулирует конечную точку, которую они должны вызвать, запускает саги для обработки жизненного цикла HTTP (запуск, сбой, успех и т. Д.) И т. Д.on.

Со временем я написал код, который абстрагирует большую часть стандартного шаблона в служебные функции, включая функции, которые автоматически генерируют создатели действий, селекторы, редукторы и функцию connect.Это куча кода, несколько запутанная, но суть выглядит примерно так.Во-первых, я настроил массив объектов, описывающих действия, которые возможны для этого узла-редуктора.Упрощенная версия выглядит следующим образом:

const messages = [
  { action: 'GET', creator: 'get', connect: true },
  { action: 'POST', creator: 'post', connect: true },
  { action: 'CLEAR', creator: 'clear', connect: true },
];

Здесь описывается три действия: get, post и clear, и что они должны быть выставлены в разъеме.У меня есть набор общих редукторов (например, большинство get редукторов одинаковы для разных узлов), поэтому они предполагаются здесь на основе имени.

Затем я настраиваю список селекторов, например: const selectorKeys = ['data','pending','errors'];

... а затем у меня есть заводская функция, в которую я подаю эти массивы, которая выглядит примерно так:

const { connector } = stateGenerators({
   keyword: 'users', //redux node keyword
   messages: messages,
   selectorKeys: selectorKeys

}) 

Это упрощенная версия того, как все действительно работает, ноэто суть этого.Опять же, весь приведенный выше код абстрагируется в файл состояния, например state-users.js.

Затем в моем компоненте класса мне просто нужно импортировать connector из state-users.js, например так:

import { connector } from 'state-users';

class Users extends Component {
    componentDidMount() {
       this.props.get();
    }
    componentWillUnmount() {
      this.props.clear();
    }
    render () {
      const { data } = this.props;
      return (
        <div>
        {data.map()}
        </div>

      )
    }
}

export connector()(Users)

Эта модель иногда становится неуклюжей, но приятно то, что почти весь шаблон редукса абстрагирован в общие файлы, поэтому мои отдельные файлы состояний, по большей части, действительно просты.

Теперь к вопросу: возможно ли сделать что-то вроде этого подхода «фабричной функции» с крючками?До сих пор в своих экспериментах я не понял, как это сделать.Есть две фундаментальные проблемы:

  • Во-первых, вы не можете помещать хуки в циклы, поэтому я не могу сделать это:
const selectors = {}
const reduxNodeKeyword = 'users';
['data','pending','errors'].map((selectorKey) => {
   selectors[selectorKey] = useSelector((state) => state[keyword].selectorKey);
})

Этот код приводит кэта ошибка: React hook "useSelector" cannot be called inside of a callback.

На практике это означает, что я не могу просто передать массив клавиш выбора, который мне нужен, и затем заставить его плюнуть на мои селекторы.

  • Во-вторых, вы не можете помещать крючки в условные выражения.Так как первая идея провалилась, я попробовал другой подход в своей заводской функции, который выглядит примерно так:
 if (_.includes(stateSelectors, 'data')) {
    result['data'] = useSelector((state) => state[keyword].data);
 }

Это приводит к этой ошибке: React hook "useSelector" is called conditionally. React Hooks must be called in the exact same order in every component render

Так что это тоже облом.Но я думаю, что у меня осталось то, что для каждого из моих 100 узлов Redux мне пришлось бы написать свой собственный и подробный крючок для более или менее повторяющегося connect.

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

Помощь?

Спасибо!

1 Ответ

0 голосов
/ 02 июля 2019

Не уверен, будет ли это полезно для других, но я нашел подход, который работает для меня. Я не могу поставить хуки в итераторах или в операторах if, но у меня есть общие шаблоны повсюду. Таким образом, ответ состоял в том, чтобы абстрагировать эти общие шаблоны в общие крючки. Например, у меня есть хук с именем useCommonReduxListSelectors, который выглядит так:

export const useReduxListCommonSelectors = (keyword: string) => {
  return {
    data: useSelector((state: any) => state[keyword].data),
    errorMessage: useSelector((state: any) => state[keyword].errorMessage),
    filters: useSelector((state: any) => state[keyword].filters),
    pending: useSelector((state: any) => state[keyword].pending),
    totalCount: useSelector((state: any) => state[keyword].totalCount)
  };
};

У меня есть множество узлов состояния Redux, которые отвечают за обработку списков, возвращаемых из API, и форма данных большинства этих конечных точек списка - это то, что вы видите выше. Итак, ловушка, которую вызовет компонент, использует useReduxListCommonSelectors, например:

export const useReduxState = ({ id, defaultValues }) => {
  const selectors = useReduxListCommonSelectors({
    keyword:'users'
  });

  return {
    ...selectors,

  };
};

И, очевидно, useReduxState может содержать любые другие данные (actionCreators, пользовательские селекторы и т. Д.), Которые требуются этому узлу. Таким образом, это позволяет мне абстрагировать наиболее распространенные шаблоны в многократно используемые хуки, а также иметь возможность добавлять собственный код к каждому экземпляру useReduxState по мере необходимости.

...