likes
comments
collection
share

手把手教你写一个极简React全局状态管理器

作者站长头像
站长
· 阅读数 13

前言

大家好这里是阳九,一个中途转行的野路子码农,热衷于研究和手写前端工具.

我的宗旨就是 万物皆可手写

新手创作不易,有问题欢迎指出和轻喷,谢谢

本文章适合有一定React开发经验,并且对redux这类写起来烦的一匹的库深恶痛绝的前端开发

Redux难用?

没错,redux确实难用, 这玩意核心源码只有区区几百行,意味着它偏向底层(但是居然有人为了这几百行代码写一整本书= =)

而如果你是一些小公司,使用redux必将会大大增加开发工作量。

我们的需求就是,来一个新手前端,只需要看三分钟文档就会用!!!

自定义store用法

import Store from './store'

const store = new Store({
    age:18,
    name:'张三'
    }) 
   
function App(){
    const {age} = store.useState(['age'])
    
    const addAge = ()=>{store.setState({age:age+1}) }
    
    return ( 
            <div>
                <div>{age}</div>
                <button onClick={addAge}>全局增加age</button>
            </div>
            )
} 

利用useState实现全局状态管理

我们知道 useState返回了setState方法 那么当组件使用全局状态时,将这个setValue函数保存到数组里,再批量执行,不就实现了全局状态更新?

const [value,setValue] = useState(0)

自定义store.useState

我们可以在使用store.useState时,将React.useState的setValue方法取出并保存起来 保存到store的_event中, 也就是on方法

自定义store.useState方法

class Store{
    private _events: Record<string, Function[]> = {}; // 保存所有的updater key为状态名
    private _state: any = {};  // store中保存的state
    ...
   useState(keys: string[]) {
        // 使用useState 保存状态
        const [value, setValue] = useState(this._state);
        //创建updater   
        const updater = (newVal: any) => {
            setValue(newVal);
        };

        // 将使用的states作为依赖  统一注册updater(首屏时即注册)
        useEffect(() => {
            keys.forEach((key) => {
                this.on(key, updater);
            })

            // 组件销毁时  统一销毁所有的updater
            return () => {
                keys.forEach((key) => {
                    this.off(key, updater);
                })
            };
        }, keys);


        return value;
    };
}

on和off方法

这两个方法用于保存和销毁updater(组件的setValue)

// 将一个updater保存到_event中  
    on(key: string, updater: Function): void {
        const s = this._events[key]

        if (!s) { // 如果没有updater数组则创建一个数组
            this._events[key] = [updater];
        } else if (s.indexOf(updater) < 0) {//添加一个updater到数组中
            s.push(updater);
        }
    }

    // 从_event[key]中移除updater
    off(key: string, updater: Function): void {
        const s = this._events[key];
        this._events[key] = [updater]

        if (s) {
            const index = s.indexOf(updater);
            if (index >= 0) {
                s.splice(index, 1);
            }
        }
    }

自定义store.setState方法

此时我们已经有了使用全局状态组件的updater,批量执行即可

setState(newStates: Record<string, any>): void {
        const needUpdateKeys: any[] = []; // 需要更新的状态

        // 遍历对比 检出需要更新的状态 
        Object.keys(newStates).forEach((key) => {
            if (this._state[key] !== newStates[key]) {
                needUpdateKeys.push(key);
                this._state[key] = newStates[key];
            }
        })

        // 更新使用这些state的组件
        if (needUpdateKeys.length > 0) {
            this.updateComponents(needUpdateKeys);
        }
    }

更新组件updateComponents

 // 遍历所有需要更新的状态  批量执行_event数组内保存的updater, 更新所有组件
    updateComponents(keys: string[]) {
        keys.forEach((key) => {
            const updaters: any[] = this._events[key];

            if (Array.isArray(updaters)) {
                updaters.forEach((updater) => {
                    updater(this._state[key]);
                })
            }
        })
    }

完整代码

import { useState, useEffect } from 'react';


export class Store {
    private _events: Record<string, Function[]> = {}; // 保存所有的updater key为状态名
    private _state: any = {};  // store中保存的state

    constructor(initStates: Record<string, any>) {
        this._state = initStates
    }

    // _events中保存所有的updater  
    on(key: string, updater: Function): void {
        const s = this._events[key]

        if (!s) { // 如果没有updater数组则创建一个数组
            this._events[key] = [updater];
        } else if (s.indexOf(updater) < 0) {//添加一个updater到数组中
            s.push(updater);
        }
    }

    // 从_event[key]中移除updater
    off(key: string, updater: Function): void {
        const s = this._events[key];
        this._events[key] = [updater]

        if (s) {
            const index = s.indexOf(updater);
            if (index >= 0) {
                s.splice(index, 1);
            }
        }
    }
    
    setState(newStates: Record<string, any>): void {
        const needUpdateKeys: any[] = []; // 需要更新的状态

        // 遍历对比 检出需要更新的状态 
        Object.keys(newStates).forEach((key) => {
            if (this._state[key] !== newStates[key]) {
                needUpdateKeys.push(key);
                this._state[key] = newStates[key];
            }
        })

        // 更新使用这些state的组件
        if (needUpdateKeys.length > 0) {
            this.updateComponents(needUpdateKeys);
        }
    }

    useState(keys: string[]) {
        // 使用useState 保存状态
        const [value, setValue] = useState(this._state);
        //创建updater   
        const updater = () => {
            setValue(this._state);
        };

        // 将使用的states作为依赖  统一注册updater(首屏时即注册)
        useEffect(() => {
            keys.forEach((key) => {
                this.on(key, updater);
            })

            // 组件销毁时  统一销毁所有的updater
            return () => {
                keys.forEach((key) => {
                    this.off(key, updater);
                })
            };
        }, keys);


        return value;
    };

    // 遍历所有需要更新的状态  批量执行_event数组内保存的updater, 更新所有组件
    updateComponents(keys: string[]) {
        keys.forEach((key) => {
            const updaters: any[] = this._events[key];

            if (Array.isArray(updaters)) {
                updaters.forEach((updater) => {
                    updater(this._state[key]);
                })
            }
        })
    }
}

后记

当然这里只是提供一个思路,来自己封装全局状态管理器,本次的核心思想就是:保存所有组件的setState函数,统一执行. 当然也可以根据项目添加各种功能,比如函数式修改state,副作用单独处理和拦截器等等。

这里是一个我写的另一个使用观察者模式实现的全局状态管理器,是另一种思路,大家可以参考,核心结构参考了react-Query库

github.com/lzy19926/lz…