对全局状态做出反应而没有上下文或没有缺点?

时间:2020-08-01 19:47:26

标签: javascript reactjs redux react-state-management

我最近在下面的文章State Management with React Hooks — No Redux or Context API中进行游戏。由于从一开始就做出反应,最受关注的问题始终是状态管理和全局状态。 Redux已成为流行的选择,最近成为上下文API。但是这种方法似乎更容易,代码更少,可扩展性更高。

我的问题是,有人能看到使用我可能忽略的这种状态管理方法的弊端吗?我对代码进行了一些调整以支持SSR,它可以在Nextjs中使用,并且使操作和状态变量的设置更加友好。

代码如下:

Codesandbox Demo

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的重新渲染问题,但是在小块中这应该不是问题。

1 个答案:

答案 0 :(得分:0)

很好的方法,但是对于大型应用程序,尤其是在性能方面,我仍然认为redux更好。使用您的方法的一个示例是,将按钮添加为单独的组件,同时用React.memo进行包装,并从按钮组件中触发actions.toggle(),但是该按钮会重新渲染2次,但不会在更改后继续状态。

因此,在构建大型应用程序时,您总是希望通过删除不必要的重新渲染来提高性能,但这不是这种情况。

这是我的分析,谢谢您的工作。

代码showcase