Реагировать на глобальное состояние без контекста или избыточных недостатков?

Недавно я прочитал следующую статью Управление состоянием с помощью React Hooks — без Redux или Context API. С самого начала реакции наиболее обсуждаемой проблемой всегда было управление состоянием и глобальное состояние. Популярным выбором был Redux, а в последнее время — контекстный API. Но этот подход кажется намного проще, меньше кода и более масштабируемым.

Мой вопрос: может ли кто-нибудь увидеть обратную сторону использования этого типа подхода к управлению состоянием, который я, возможно, упустил из виду. Я немного подправил код для поддержки SSR, и он работает в Nextjs, а также сделал его более удобным для использования действий и настройки переменной состояния.

Вот код:

Демонстрация Codesandbox

useGlobalState.js

import React, { useState, useEffect, useLayoutEffect } from 'react';

const effect = typeof window === 'undefined' ? useEffect : useLayoutEffect;

function setState(newState) {
    if (newState === this.state) return;
    this.state = newState;
    this.listeners.forEach((listener) => {
        listener(this.state);
    });
}

function useCustom() {
    const newListener = useState()[1];
    effect(() => {
        this.listeners.push(newListener);
        return () => {
            this.listeners = this.listeners.filter((listener) => listener !== newListener);
        };
    }, []);
    return [this.state, this.setState, this.actions];
}

function associateActions(store, actions) {
    const associatedActions = {};
    if (actions) {
        Object.keys(actions).forEach((key) => {
            if (typeof actions[key] === 'function') {
                associatedActions[key] = actions[key].bind(null, store);
            }
            if (typeof actions[key] === 'object') {
                associatedActions[key] = associateActions(store, actions[key]);
            }
        });
    }
    return associatedActions;
}

const useGlobalHook = (initialState, actions) => {
    const store = { state: initialState, listeners: [] };
    store.setState = setState.bind(store);
    store.actions = associateActions(store, actions);
    return useCustom.bind(store, React);
};

export default useGlobalHook;

Затем настройте собственный хук для переменной состояния, это может быть простая строка или объект, вот простой:

import useGlobalState from './useGlobalState';

const initialState = 'Hi';

// Example action for complex processes setState will be passed to component for use as well
const someAction = (store, val) => store.setState(val); 

const useValue = useGlobalState(initialState, { someAction });

export default useValue;

И используйте в компоненте:

import React from 'react'
import useVal from './useVal'

export default () => {
  const [val, setVal, actions] = useVal();

  const handleClick = () => {
    setVal('New Val');
    // or use some actions
    actions.someAction('New Val');
  }

  return(
    <div>{val}</div>
    <button onClick={handleClick}>Click Me</button>
  )
}

Все это кажется гораздо более чистым и простым подходом, и мне интересно, почему это не подход к управлению состоянием в реакции. Во-первых, вам не нужно оборачивать все в провайдера. Кроме того, его чрезвычайно легко реализовать, и в фактическом приложении задействовано гораздо меньше кода. Может ли кто-нибудь увидеть недостатки в использовании этого подхода. Единственное, о чем я могу думать, это проблема повторного рендеринга, которую имеет API контекста, но в небольших фрагментах это не должно быть проблемой.


person user3331344    schedule 01.08.2020    source источник


Ответы (1)


хороший подход, но я все же вижу, что redux лучше для больших приложений, особенно когда речь идет о производительности. Примером использования вашего подхода является добавление кнопки в качестве отдельного компонента при обертывании ее React.memo и запуске actions.toggle() из компонента кнопки, но кнопка перерисовывается 2 раза, что не влияет на измененное состояние.

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

это мои анализы, спасибо за вашу работу.

здесь код витрина

person adel    schedule 01.08.2020
comment
Я использовал шаблон реакции codeandbox по умолчанию, в котором React.StrictMode обертывал приложение в index.js, что вызывало двойной рендеринг. Если вы удалите его в своем примере в index.js, он больше не будет дублировать рендеринг. - person user3331344; 02.08.2020
comment
я имел в виду, что компонент Button не передает состояние, поэтому он не должен повторно отображаться при обновлении состояния при обертывании его с помощью react.memo, и здесь это не так. - person adel; 02.08.2020