This package has been deprecated

Author message:

Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.

@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>

Package Sidebar

Install

npm i @budarin/use-react-redux

Weekly Downloads

5

Version

1.1.13

License

MIT

Unpacked Size

154 kB

Total Files

11

Last publish

Collaborators

  • budarin