UNPKG

@zjxpcyc/react-tiny-store

Version:
196 lines (165 loc) 5.17 kB
/* * Copyright (c) [2020] Zhang Yansen.All rights reserved. * * react-tiny-store is licensed under the Mulan PSL v2. * You can use this software according to the terms and conditions of the Mulan PSL v2. * You may obtain a copy of Mulan PSL v2 at: * * http://license.coscl.org.cn/MulanPSL2 * * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PSL v2 for more details. */ import React from 'react'; import observe from '@zjxpcyc/observe-ext'; /** * 创建一个 store * 使用场景:需要将部分动做脱离 react 组件,在外部进行。使用 context 返回值可以任意操作 * * @param {*|function} initialState - 初始 state 或者 定义 action 的函数 * @param {function} enhancer - 插件或者用于扩展的函数 * @returns {Object} 返回 context */ export function createStore(initialState, enhancer) { const initialEnhancer = initEnhancerCreator(initialState); return observe(compose(enhancer, hookEnhancer, initialEnhancer, stateEnhancer)); } /** * 创建一个 store * 使用场景:需要在 react 组件内进行各种 动作执行 * * @param {*|function} initialState - 初始 state 或者 定义 action 的函数 * @param {function} enhancer - 插件或者用于扩展的函数 * @returns {hook} 返回 store hook */ export function create(initialState, enhancer) { return createStore(initialState, enhancer).useStore; } /** * 初始化函数 Enhancer 的创建者,该 Enhancer 用于初始化状态或提供一个自定义的动作集合。 * @param {*|Function} initialState - 初始状态对象或一个函数,函数接收context作为参数。 * @returns {Function} 返回一个 Enhancer 。 */ function initEnhancerCreator(initialState) { return function(context) { let actions = context.setState; if (typeof initialState == 'function') { actions = initialState(context) || context.setState; } else { context.setState(initialState); } return { ...context, actions, }; } } /** * 创建并管理状态的 Enhancer * * @param {Object} context - 上下文对象,至少包含一个publish函数 * @returns {Object} 返回一个 Enhancer */ function stateEnhancer(context) { const { publish } = context; // 状态 let state = undefined; /** * 更新状态函数 * * @param {*} newState */ const getState = () => state; /** * 更新状态函数 * * @param {*} newState */ const setState = (newState) => publish(state = typeof newState == 'function' ? newState(state) : newState); return { ...context, getState, setState, } } /** * hookEnhancer 用来生成 react hook * * @param {Object} context * @returns {Function} 返回 store hook */ function hookEnhancer(context) { // 小于 React 18 的版本,使用一个简易的 shim const useSyncExternalStore = React.useSyncExternalStore || useSyncExternalStoreShim; /** * 最终返回的 hook */ function useStore(selector, filterActions) { // 构造 actions const actions = React.useMemo(() => { return typeof filterActions == 'function' ? filterActions(context.actions) : context.actions; }, []); // 构造 getSnapshot 函数 const getSnapshot = React.useCallback(() => { const snapshot = context.getState(); return typeof selector == 'function' ? selector(snapshot) : snapshot; }, []); const state = useSyncExternalStore(context.subscribe, getSnapshot); const result = React.useMemo(() => ([ state, actions, {...context}, ]), [state]); return result; } return { ...context, useStore, } } /** * Composes multiple functions from right to left. * * @param {...Function} funcs - Functions to be composed. * @returns {Function} The composed function. */ export function compose(...funcs) { const fs = funcs.filter(Boolean); if (fs.length === 0) { return (arg) => arg; // Return identity function if no functions provided } if (fs.length === 1) { return fs[0]; // Return the single function if only one is provided } return fs.reduce((acc, curr) => { return (...args) => acc(curr(...args)); }); } /** * useSyncExternalStore 的简易实现 * * @param {Function} subscribe * @param {Function} getSnapshot * @returns useSyncExternalStore */ function useSyncExternalStoreShim(subscribe, getSnapshot) { const initialState = React.useMemo(getSnapshot, []); const [value, setValue] = React.useState(initialState); const valueRef = React.useRef(); valueRef.current = value; React.useEffect(() => { const listener = () => { const newVal = getSnapshot(); if (!Object.is(valueRef.current, newVal)) { setValue(newVal); } } // 防止 从初始化 value 到 useEffect 这个时间段内,有 state 更新 // 所以此处执行一次 listener listener(); return subscribe(listener); }, []); return value; }