@budarin/use-react-redux
    TypeScript icon, indicating that this package has built-in type declarations

    1.1.13 • Public • Published

    @budarin/use-react-redux

    English Version

    Установка

    npm install --save @budarin/use-react-redux

    Что это ?

    Высокопроизводительная библиотека управления состоянием приложения, реализованная на React.Context и React.Hooks.

    Статья с описанием реализации данной библиотеки - React Redux на React.Hooks+React.Сontext.

    Зачем ?

    Пакет react-redux представляет из себя один большой костыль: решая полезную задачу управлением состоянием приложения, он порождает проблемы, с которыми ему же и приходится бороться:

    • проблема скрещивания синхронного состояния redux, хранящегося вне react, с асинхронным циклом отрисовки React
    • проблемы zombie children и stale props.

    Размер redux + react-redux также довольно большой - около 16 кб минифицированного кода и около 8 кб - сжатого.

    Поэтому требуется родное для React решение для управления глобальным состоянием приложения, которое хранит состояние в React и управляется им же.

    Так же назрела необходимость иметь не только глобальное состояние приложения, но и локальные хранилища для динамически загружаемых страниц - повозившись с react-redux можно это реализовать, но наша реализация проще.

    Как это работает ?

    Библиотека использует хранение и изменение состояния исключительно в контекте React, используя React.Context и React.Hooks.

    Высокая производительность ее достигается за счет использования внутри нее не документированных возможностей React.createContext API, которые позволяют избежать вызова рендера всех компонент, где используется доступ к React.Context, в котором произошли изменения.

    Благодаря React.Hooks под капотом производится вызов рендера только тех компонент, которые были подписаны на те изменения, которые произошли в контексте.

    Данный функционал контекста с подписками реализован в пакете use-context-selection.

    const state = {
        a: 'A value',
        b: 'B value',
        c: 'B value',
    };
    
    // теперь в компоненте А можно слушать только изменени `a` в state
    const a = useContextSelection((state) => state.a);

    Ниже представлен результат профилировки добавления узла в дерево - подтверждает переисовку только модифицированного узла, а не всех узлов, как при использовании стандартного React.Context:

    Как использовать

    app-store.js

    import { createStorage } from '@budarin/use-react-redux';
    
    const { useStore, StoreProvider } = createStorage();
    
    export const useAppStore = useStore;
    export const AppStoreProvider = StoreProvider;

    опишем наш логирующий middleware

    middlewares.js

    const loggerMiddleware = (store) => (next) => (action) => {
        console.log('action', action);
        return next(action);
    };
    
    export const appMiddlewares = [loggerMiddleware];

    опишем составляющие нашего redux хранилища

    ducks.js

    export const initialState = { counter: 0 };
    
    export const reducer = (state = initialState, action) => {
        switch (action.type) {
            case 'INCREMENT':
                return { counter: state.counter + 1 };
            case 'DECREMENT':
                return { counter: state.counter - 1 };
            default:
                return state;
        }
    };
    
    export const selector = (state) => state;
    
    export const actions = {
        increment: () => ({ type: 'INCREMENT' }),
        decrement: () => ({ type: 'DECREMENT' }),
    };

    опишем компонент Counter

    Counter.jsx

    export const Counter = ({ props: { counter }, actions }) => (
        <div>
            <p>
                Clicked: {counter} times
                {'  '}
                <button onClick={actions.increment}>+</button>
                {'  '}
                <button onClick={actions.decrement}>-</button>
            </p>
        </div>
    );

    осталось реализовать приложение

    app.js

    import { useAppStore, AppStoreProvider } from './app-store';
    
    import { Counter } from './Counter';
    import { appMiddlewares } from './middlewares';
    import { initialState, reducer, actions, selector } from './ducks';
    
    const CounterContainer = (containerProps) => {
        const props = useAppStore({ selector, actions, containerProps });
        return <Counter {...props} />;
    };
    
    export default const App = () => (
        <AppStoreProvider
            reducer={reducer}
            initialState={initialState}
            middlewares={appMiddlewares}
        >
            <CounterContainer />
        </AppStoreProvider>
    );

    Вот и все!

    Размер подключаемого минифицированного кода около 4.5 кб и ~2 кб в сжатом виде.

    Библиотека полностью консистентна в Concurent Mode и даже немного более производительна чем react-redux !

    При разработке приложения нужно лишь уделять внимание мемоизации результатов рендера контейнера.

    Приятной вам разработки! 😊

    API

    Экспортируемые методы:

    Генерируемые хуки и компоненты:

    batch

    Под капотом используется unstable_batchedUpdates() API - группирует несколько обновлений в React и отрисовывает за один раз. В контексте исполнения React - ее бесполезно использовать - React сам под капотом оптимизирует множественные последовательные изменения состояния, объединяя их в одно. В основном данная функция должна использоваться когда состоянием управляют вне контекста React (websockets и тому подобное).

    Param Type Description Optional / Required
    callback void Callback, в котором вызываются методы, изменяющие состояние приложения Required

    Для примера выполним увеличение счетчика в 3 шага: инкремент декримент и снова инкремент счетчика. В результате вызова всех трех изменений состояния приложения в методе batch - произойдет не три рендера, а один.

    import { batch } from '@budarin/use-react-redux';
    
    window.setTimeout(
        () =>
            batch(() => {
                dispatch(actionCreators.increment());
                dispatch(actionCreators.deccrement());
                dispatch(actionCreators.increment());
            }),
        3000,
    );

    createStorage

    Функция, которая создает хук useStore и компонент StoreProvider для доступа к новому хранилищу.

    • Возвращаемое значение: объект { useStore, StoreProvider }

    Пример

    const { useStore, StoreProvider } = createStorage();

    useStore

    Хук, который подключает контейнер к хранилищу для получения данных и отсылки ему actions.

    Входной параметр - объект:

    Param Type Description Optional / Required
    selector Function функция селектор, для выборки данных из состояния Optional
    actions Function / Object объект из функций генераторов событий или функция, создающая объект генераторов событий Optional
    containerProps any свойства, пробрасываемые контейнеру Optional
    • Возвращаемое значение: объект
      Param Type Description Optional / Required
      props object результирующие свойства контейнера, полученные как объединение: собственных свойств контейнера + свойств, полученных из состояния приложения + свойств, полученных из генераторов событий для отправки actions в stor при помощи dispatch Optional
      actions Object Объект, содержащий методы, вызывающие события Optional
      dispatch Dispatch Метод dispatch хранилища данных Required

    Пример

    const { props, actions, dispatch } = useStore({ selector, actions, containerProps });

    StoreProvider

    Компонент-провайдер для оборачивания приложения, с целью проброса Context внутрь дерева компонентов React

    Param Type Description Optional / Required
    initialState object объект, хранящий состояние приложения Optional
    reducer reducer[] редьюсер для формирования состояния приложения Required
    appMiddlewares middleware[] массив функций middleware Optional

    Пример

    <StoreProvider reducer={reducer} initialState={initialState} middlewares={appMiddlewares}>
        <App />
    </StoreProvider>

    Install

    npm i @budarin/use-react-redux

    DownloadsWeekly Downloads

    138

    Version

    1.1.13

    License

    MIT

    Unpacked Size

    154 kB

    Total Files

    11

    Last publish

    Collaborators

    • budarin