使用Global State Hook取代Redux

从1年半前开始使用React Hook,看着大串大串的reducer方法,我在做项目的时候在想,redux这样的模式是否过于繁琐,比如说给管道写数据,非得调用一个dispatch方法,另外还要做一个reducer的数据合并方法,加上各种中间件容易整出一些头疼的代码。

Redux本质上是一种全局订阅模式

图.001.jpeg

如图所示,redux提供了一个订阅管道,ComponentA / ComponentB / ComponentC 通过dispatch给redux写数据,reducer合并处理数据,保存到redux states,一旦数据改变订阅数据的组件做响应式更新。

react 16.8版本推出了hook写法,出现了useState的写法,我们能不能推出一种新的简化的写法取代Redux。

Global State 是不错的方案,国外有一些版本,我这里也给出我的版本。

图.002.jpeg

就是这么简单的一段(^_^)

// global-state.ts
import { useEffect, useState } from 'react';

declare const process: any;
export const GLOBAL_STATE = {
  DEBUG: false
};
export const makeGlobalState = <T extends unknown>(initValue: T, name?: string, filter?: (val: T) => T) => {
  const globalStates: any[] = [];
  let currentValue = initValue;

  return (): [T, (val: T) => void] => {
    const [value, setValue] = useState(currentValue);
    if (!globalStates.includes(setValue)) {
      globalStates.push(setValue);
    }

    useEffect(
      () => () => {
        if (process.env.NODE_ENV && process.env.NODE_ENV === 'development') {
          globalStates.splice(globalStates.indexOf(setValue) - 1, 2);
        } else {
          globalStates.splice(globalStates.indexOf(setValue), 1);
        }
      },
      []
    );

    const setAllStateValue = (value: T) => {
      if (filter) {
        value = filter(value);
      }
      if (GLOBAL_STATE.DEBUG) {
        console.log('SET ' + name, value);
      }
      currentValue = value;
      globalStates.forEach((setVal) => {
        setVal(value);
      });
    };
    return [value, setAllStateValue];
  };
};

复制代码

做法是给每个component的local state做一个收集器,把setState的方法收集起来,在每次调用这个global state的设置方法的时候,遍历执行所有的set state,达到给所有component刷新的目的。

  • 有添加就要有销毁,代码中的useEffect用于在component销毁时移除setState方法。这里要注意的是因为development模式下component渲染2次(会添加2次),所以这里需要做一些处理。

  • 函数makeGlobalState还提供了一个filter功能,可以做类似reducer处理数据的功能,说得高大上一点就是可以定义一个中间件什么的….。第二个参数name则是用于debug打印。

使用的时候,先要创建一份文件,去定义所有的global-state,给初始值。

// my-global-states.ts
import { makeGlobalState } from './global-state';

export const useCounter = makeGlobalState(1, 'Counter', (val) => {
  return val > 10 ? 10 : val;
});

export const useUserInfo = makeGlobalState({ name: 'John', age: 30 });

复制代码

然后就可以在不同的component上使用了

// CompA.tsx
import React from 'react';
import { useCounter } from './my-global-states';

export const CompA = () => {
  const [count, setCount] = useCounter();
  return (
    <div>
      <div>{count}</div>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        加1
      </button>
    </div>
  );
};


复制代码
// CompB.tsx
import React from 'react';
import { useCounter } from './my-global-states';

export const CompB = () => {
  const [count, setCount] = useCounter();
  return (
    <div>
      <div>{count}</div>
      <button
        onClick={() => {
          setCount(5);
        }}
      >
        设为5
      </button>
    </div>
  );
};
复制代码
// App.tsx
import React from 'react';
import './App.css';
import { CompA } from './CompA';
import { CompB } from './CompB';
import { GLOBAL_STATE } from './global-state';
GLOBAL_STATE.DEBUG = true;

function App() {
  return (
    <div className="App">
      <div>
        <CompA />
      </div>
      <div>
        <CompB />
      </div>
    </div>
  );
}

export default App;
复制代码

运行结果,任何一个使用了useCounter的组件调用了 setCounter 方法,其他的订阅了couter值的组件也一起刷新了。

Screen Shot 2021-05-08 at 17.56.22.png

Global State 使用起来会比Redux简单很多,非常推荐大家使用。
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享