Solidjs状态更新管理在React中的应用

2,674 阅读2分钟
Solidjs状态更新
interface Running {
    execute: () => void;
    dependencies: Set<Set<Running>>;
}

const context = [];

function subscribe(running: Running, subscriptions: Set<Running>) {
    subscriptions.add(running);
    running.dependencies.add(subscriptions);
}

export function createSignal<T>(value: T) {
    const subscriptions = new Set<Running>();

    const read = (): T => {
        const running = context[context.length - 1];
        if (running) subscribe(running, subscriptions);
        return value;
    };

    const write = (nextValue: T) => {
        value = nextValue;
        for (const sub of [...subscriptions]) {
            sub.execute();
        }
    };
    return [read, write];
}

function cleanup(running) {
    for (const dep of running.dependencies) {
        dep.delete(running);
    }
    running.dependencies.clear();
}

export function createEffect(fn: () => void | unknown) {
    const execute = () => {
        cleanup(running);
        context.push(running);
        try {
            fn();
        } finally {
            context.pop();
        }
    };

    const running = {
        execute,
        dependencies: new Set<Set<Running>>(),
    };

    execute();
}

export function createMemo(fn: () => void | unknown) {
  const [s, set] = createSignal();
  createEffect(() => set(fn()));
  return s;
}
添加一个批量更新的函数
function flush(fn) {
  if (typeof MessageChannel !== undefined) {
    const { port1, port2 } = new MessageChannel();
    port1.onmessage = fn;
    port2.postMessage(null);
  } else {
    setTimeout(fn);
  }
}
借用上述更新原理我们可以在 React中应用封装一个状态管理

export function createStore(initState) {
  const createSetter = {};
  const stateKeys = Object.keys(initState);
  for (let i = 0; i < stateKeys.length; i++) {
    const [get, set] = createSignal(initState[stateKeys[i]]);
    createSetter[stateKeys[i]] = {get,set}
  }

  const dispatch = (state) => {
    const dispatchKeys = Object.keys(state);
    dispatchKeys.forEach(item => createSetter[item].set(state[item]));
  }

  const getStore = () => Object.keys(createSetter).reduce((pre,cur) => { 
    pre[cur] = createSetter[cur].get();
    return pre;
  }, {})

  const useStore = (stateKeys) => {
    const [, forceUpdate] = useState({});
    const updateRef = useRef(false);
    const queue = useRef(0);
    useEffect(() => {
      createEffect(() => {
        stateKeys.forEach(item => {
          createSetter[item].get()
        });
        if (updateRef.current) {
          queue.current += 1;
          queue.current === 1 && flush(() => {
            queue.current = 0;
            forceUpdate({});
          })
        } else {
          updateRef.current = true;
        }
      });
    }, []);

    return getStore()
  }

  return { useStore, getStore, dispatch };
}
利用上述状态管理一个小例子

const { useStore, getStore, dispatch } = createStore({
  count: 0,
  grade: 0,
  num: 0,
});

const handleAdd = () => {
  const { count, grade, num } = getStore();
  Promise.resolve().then(() => {
    dispatch({
      count: count+1,
      grade: grade+1,
      num: num + 1,
    })
  })
}

const handleGrade = () => {
  const { grade } = getStore();
  dispatch({
    grade: grade+1,
  })
}

function Counter() {
  const { count, grade } = useStore(['count', 'grade']);

  console.log(count, 'render----123count')
  return (
    <div>
      <div>{count}-count</div>
      <div>{grade}-grade</div>
      <div onClick={handleAdd}>add</div>
    </div>
  );
}

function Grade() {
  const { grade, num } = useStore(['grade', 'num']);

  console.log(grade, 'render----123grade')
  return (
    <div>
      <div>{grade}-grade</div>
      <div>{num}-num</div>
      <div onClick={handleGrade}>grade</div>
    </div>
  );
}

function App() {
  return (
    <div className="App">
     <Counter />
     <Grade />
    </div>
  );
}

借用上述更新原理我们可以在 React中应用封装另外一个状态管理
export function createStore<T>(initState: T) {
    const storeContext = createContext<Record<string, any>>({});

    const getStore = (createSetter) =>
        Object.keys(createSetter).reduce((pre, cur) => {
            pre[cur] = createSetter[cur].get();
            return pre;
        }, {});

    const useStore = (createSetter, stateKeys) => {
        const [, forceUpdate] = useState({});
        const updateRef = useRef<boolean>(false);
        const queue = useRef<number>(0);
        useEffect(() => {
            createEffect(() => {
                stateKeys.forEach((item) => {
                    createSetter[item].get();
                });
                if (updateRef.current) {
                    queue.current += 1;
                    queue.current === 1 &&
                        flush(() => {
                            queue.current = 0;
                            forceUpdate({});
                        });
                } else {
                    updateRef.current = true;
                }
            });
        }, []);

        return getStore(createSetter);
    };

    const Provider = ({ children }) => {
        const [createSetter] = useState(() => {
            const getSetter = Object.create(null);
            const stateKeys = Object.keys(initState);
            for (let i = 0; i < stateKeys.length; i++) {
                const [get, set] = createSignal(initState[stateKeys[i]]);
                getSetter[stateKeys[i]] = { get, set };
            }
            return getSetter;
        });

        const dispatch = useCallback((state) => {
            const dispatchKeys = Object.keys(state);
            dispatchKeys.forEach((item) => createSetter[item].set(state[item]));
        }, []);

        const storeRef = useRef({
            useStore: useStore.bind(null, createSetter),
            dispatch,
            getStore: getStore.bind(null, createSetter),
        });

        return <storeContext.Provider value={{ storeRef }}>{children}</storeContext.Provider>;
    };

    const useSlice = <U extends keyof T>(stateKeys: U[]) => {
        const { storeRef } = useContext(storeContext);
        const { useStore, dispatch, getStore } = storeRef.current;
        return [useStore(stateKeys), dispatch, getStore];
    };

    return { Provider, useSlice };
}

从上面一个例子中我们可以看到每个组件只关心自己所关心的数据,不相关的数据更新,不会引起自身组件rerender😊