react useCreateWatchedStore 輕量級高性能狀態管理庫

import {useContext, useEffect, useMemo, useRef, useState} from 'react';
import _get from "lodash.get";
import _set from "lodash.set";
import {shallowEqual} from "../utils/shallowEqual";

export const KEY_SAVE_TICK_COUNT = 'KEY_SAVE_TICK_COUNT';

export type WatcherFn = (nextValue: any, preValue: any, storage: WatchedStore) => void;

export interface Watcher {
    path: string,
    watcher: WatcherFn,
    preValue: any
}


export class WatchedStore {
    private readonly storeData: Record<string, any>;
    private watcherList: Watcher[];
    private tickCount = 0;

    constructor(initialValues:any = {}) {
        this.storeData = initialValues;
        this.watcherList = []; // {path, watcher, preValue}
    }


    getValue(path: string) {
        return _get(this.storeData, path);
    }

    setValue(path: string, value: any) {
        this.tickCount = this.tickCount + 1;
        _set(this.storeData, path, value);
        _set(this.storeData, KEY_SAVE_TICK_COUNT, this.tickCount);
        this.notifyWatcher();
    }

    watch(path: string, watcher: WatcherFn) {
        if (typeof watcher !== "function") {
            throw 'watcher must function';
        }
        this.watcherList.push({path, watcher, preValue: undefined});
    }

    unwatch(path: string, watcher: WatcherFn) {
        this.watcherList = this.watcherList.filter((obj) => {
            return obj.path !== path && obj.watcher !== watcher;
        });
    }

    notifyWatcher() {
        const watcherList = this.watcherList || [];
        for (let i = 0; i < watcherList.length; i++) {
            const {path, watcher, preValue} = watcherList[i];
            const nextValue = this.getValue(path);
            if (!shallowEqual(nextValue, preValue)) {
                watcher(nextValue, preValue, this);
                watcherList[i].preValue = nextValue;
            }
        }
    }
}


function useCreateWatchedStore(initialValues: any = {}): WatchedStore {
    return useMemo(() => {
        return new WatchedStore(initialValues);
    }, []);
}


function useGetWatchedStore(context: any): WatchedStore {
    return useContext(context) as WatchedStore;
}


function useGetWatchedValue(context: any, path: string): any {

    const store = useGetWatchedStore(context);

    const valueRef = useRef<any>();

    const [value, setValue] = useState(() => {
        const nowValue = store.getValue(path);
        valueRef.current = nowValue;
        return nowValue;
    });


    useEffect(() => {

        const init = () => {
            const nowValue = store.getValue(path);
            if (!shallowEqual(valueRef.current, nowValue)) {
                valueRef.current = nowValue;
                setValue(nowValue);
            }
        }

        init();

        const watcher = (nowValue: any) => {
            valueRef.current = nowValue;
            setValue(nowValue);
        };

        store.watch(path, watcher);

        return () => {
            store.unwatch(path, watcher);
        }

    }, [store, path]);


    return value;
}


export {
    useCreateWatchedStore,
    useGetWatchedStore,
    useGetWatchedValue
}

  

 

 

 

import React from 'react';
import {useCreateWatchedStore, useGetWatchedValue, useGetWatchedStore} from "../hooks/useWatchedStore";

const context = React.createContext({});
const WatchedStoreProvider = context.Provider;


function SubComp1() {
    const count = useGetWatchedValue(context, 'count');
    return (
        <div>
            {count}
        </div>
    );
}


function SubComp2() {
    const store = useGetWatchedStore(context);
    const count = useGetWatchedValue(context, 'count');

    return (
        <button onClick={() => {
            const getCount = () => {
                return store.getValue("count");
            }
            store.setValue('count', getCount() + 1);
            store.setValue('count', getCount() + 1);
            store.setValue('count', getCount() + 1);
            store.setValue('count', getCount() + 1);
        }}>
            {count}
        </button>
    );
}


function DemoUseWatchedStore() {
    const store = useCreateWatchedStore({
        count: 0
    });
    return (
        <WatchedStoreProvider value={store}>
            <SubComp1/>
            <SubComp1/>
            <SubComp1/>
            <SubComp1/>
            <SubComp2/>
        </WatchedStoreProvider>
    );
}


export {
    DemoUseWatchedStore
}

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章