Библиотека react-redux Дэна Абрамова дает нам возможность подключить наше хранилище redux к нашим компонентам реакции. Это избавляет нас от необходимости передавать реквизиты полностью и дает нам некоторые оптимизации vDom бесплатно.

Единственным недостатком является необходимость написания всех этих функций mapStateToProps, но теперь, благодаря двум новым функциям React, нам, возможно, больше не придется.

Новый Context API позволяет нам совместно использовать состояние по всей иерархии компонентов. Это работает так же, как компонент Provider из react-redux, позволяя сделать хранилище доступным для всего дерева компонентов.

const MyContext = React.createContext(someInitialValue);
<MyContext.Provider value={someValueToProvide}>
  <MyApp />
</MyContext.Provider>
...
<MyContext.Consumer>
  providedValue => <MyComponent someAttribute={providedValue} />
</MyContext.Consumer>

Экспериментальный API-интерфейс React Hooks позволяет нам писать функциональные компоненты с локальным состоянием и побочными эффектами, а также получать доступ к контексту.

import React, { useState, useEffect, useContext } from 'react';
const MyComponent = () => {
  const [someValue, update] = useState(someInitialValue);
  const providedValue = useContext(MyContext);
  useEffect(() => {
    someSideEffect();
    return someCleanUpFunction; // e.g. unsubscribe
  });
  return <input type="text" value={someValue} onChange={update} />
};

Объединение встроенных хуков для создания наших собственных хуков позволяет нам извлекать и разделять общую логику.

import React, { useState, useEffect} from 'react';
const useMyHook = defaultValue => {
  const [data, update] = useState('');
  useEffect(() => {
    const callback = res => update(res);
    const unsubscribe = getData(callback);
    return unsubscribe;
  });
  return data;
};
const MyComponent = () => {
  const data = useMyHook('');
  return <div>{data}</div>
};

Конечно, наиболее распространенная логика в наших компонентах - это взаимодействие с магазином.

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

Посмотрим, как это будет выглядеть…

import Store from 'hux';
const store = createStore(reducer);

const EntryPoint = () => (
    <Store.Provider value={store}>
        <App />
    </Store.Provider>
);

… И в компоненте…

import { useDispatch, useSelector } from 'hux';

const MyComponent = id => {
    const value = useSelector(mySelector, id);
    const update = useDispatch(myActionCreator)

    return (
        <input
            type="text"
            value={value}
            onChange={e => update(e.target.value)}>
    );
}

… И здесь происходит волшебство…

import React, {useContext, useEffect, useState} from 'react';
import { compose } from 'redux';
const Store = React.createContext();
export const useDispatch = actionCreator => {
  const { dispatch } = useContext(Store);
  return compose(dispatch, actionCreator);
};
export const useSelector = (selector, ...rest) => {
  const { subscribe, getState } = useContext(Store);
  const select = () => selector(
    getState(),
    ...rest
  );
  const initial = select();
  const [value, update] = useState(initial);
  const listener = () => {
    const next = select()
    if (next !== value) {
      update(next);
    }
  };
  useEffect(() => subscribe(listener));
  return value;
}
export default Store;

Здесь мы определили два пользовательских крючка.

Первый, useDipatch, использует перехватчик useContext внутри для получения метода диспетчеризации магазина, а затем объединяет его с создателем действия, переданным из компонента. Возвращаемая им функция примет n аргументов, передаст их создателю действия и затем отправит результат.

Второй крючок немного сложнее. Мы используем ловушку useState для хранения значения, которое мы получаем от передачи текущего состояния селектору. Затем мы создаем побочный эффект с помощью хука useEffect. Наш побочный эффект - подписаться на хранилище redux, а затем обновлять локальное состояние всякий раз, когда изменяется результат нашего селектора. Метод subscribe возвращает неявную отписку, которая используется API React Hooks для последующей очистки.

Наконец, мы возвращаем полученное значение для использования в компоненте. Обратите внимание, как мы обновляем значение, возвращаемое из useSelector, только если оно не строго равно предыдущему значению. Это предотвратит повторный рендеринг компонентов, если ничего не изменилось.

Хотите это как библиотеку? Получите это от npm!