npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

use-variable-hook

v3.0.13

Published

a react hook that provide a sample store and effect management

Downloads

19

Readme

实现一个简单的状态管理/处理副作用的 hook

此hook集成了类似于useReducer的状态管理,并且加入了副作用的处理,可以理解为一个组件内部的redux+thunk中间件。

如果你需要复杂的状态管理,并且不需要在多个组件/全局 共享此状态的话,可以考虑使用此hook

使用方式:
// 声明状态数据类型
interface DataType {
  name: string
  infoList: any[]
}

// 配置状态&副作用
const variableConfig: UseVariableParams = {
  // 声明需要管理的状态和初始值
  variables: {
     name: ''
     infoList: []
  },
  // 声明副作用
  effects: {
    // 获取用户信息
    fetchuserInfo: ({ call, setLoading }, { store }, payload) => {
      // 设置loading信息,此信息可以通过useVariable返回的loading函数获取:
      // loading('fetchuserInfo')
      setLoading(true)
      // 调用call方法
      // 注意,fetchuserInfo方法为同步函数,不要在此使用async await 只需要假设call方法返回的就是用户数据即可!
      const { success, data } = call(queryUserInfoById, payload);
      const {name,infoList} = success ? data : [];
      // 使用同步的方式赋值即可
      // 这里使用proxy监听拦截store值的改变
      store.name = name
      store.infoList = infoList
      // 关闭loading
      setLoading(false);
    },
  },
};

export function UserInfoDisplayComponent({id}: {id: string}){
    // 调用useVariable hook管理用户信息
    const [{name,infoList}, dispatch, loading] = useVariable<DataType>(variableConfig);

    useEffect(()=>{
        dispatch({
            // 直接使用函数名作为type即可
            type: 'fetchuserInfo',
            payload: id
        })
    },[])

    // 使用loading(effect方法名)的方式获取loading状态
    return <Loadinglayout loading={loading('fetchuserInfo')}>
              <h2>用户姓名: {name}<h2>
              <h3>信息列表</h3>
              <ul>{infoList.map(info=><li key={info.id}>{info.data}</li>)}</ul>
           </Loadinglayout>
}
通过调用构建函数来使用常量

useVariable支持重载传入函数:

function useVariable<VariableTypes extends StoreObject>(
  creator: (...args: any[]) => UseVariableParams,
  ...args: any[]
): [VariableTypes, Dispatch, (type: Type) => boolean];

其中,creator是创建函数,useVariable内部会在初始化时调用一次creator函数,并且将args参数传入 当需要使用常量时,可以通过args传入构建函数,这个常量会以闭包的方式保存在creator函数的作用域内。

DEMO:

const creatorFn = function (form: FormInstance<any>) {
  return {
    variables: {
      /** formmodal 谈出框开关 */
      formVisiable: false,
    },
    reducers: {
      /** 打开谈出框 */
      open(store) {
        store.formVisiable = true;
      },
      /** copy */
      copy(store, { payload: initialValue }) {
        form.setFieldsValue({
          name: initialValue.name + '_copy',
          modeName: initialValue.modeName,
          description: initialValue.description,
          timeRange: [moment(initialValue.startTime), moment(initialValue.endTime)],
        });
        store.formVisiable = true;
      },
      /** 关闭谈出框 */
      close(store) {
        form.resetFields();
        store.formVisiable = false;
      },
    },
    effects: {
      /** 提交表单 */
      submit({ call, setLoading, Control }, { dispatch }, formValues) {
        setLoading(true);
        const { success } = call(createSystemReport, {
          name: formValues.name,
          modeName: formValues.modeName,
          startTime: moment(formValues.timeRange[0])?.format('YYYY-MM-DD HH:mm:ss'),
          endTime: moment(formValues.timeRange[1])?.format('YYYY-MM-DD HH:mm:ss'),
          description: formValues.description,
        });

        if (success) {
          message.success('任务创建成功!');
          dispatch({ type: 'close' });
          Control.return({});
        } else {
          message.error('任务创建失败!');
          Control.error({});
        }
        setLoading(false);
      },
    },
  } as UseVariableParams;
};


function Component(){
  const [form] = Form.useForm();

  const [{ formVisiable }, dispatch, loading] = useVariable<GenerateReportBtnType>(
    creatorFn,
    form,
  );

  useImperativeHandle(
    ref,
    () => {
      return {
        copy: (initialValue: any) => {
          dispatch({ type: 'copy', payload: initialValue });
        },
      };
    },
    [],
  );
}

如上,我们可以将form对象传入creator函数中,在copy reducer调用中,可以访问该对象

修改状态

声明 variables 后,useVariable 会自动生成 useXXX 的 reducer 用来修改变量值 例如:

// 声明变量
useVariable <
  DataType >
  {
    variables: {
      name: '',
      userInfo: [],
    },
  };

useVariable 会自动生成两个 reducer: setName , setUserInfo 其命名格式为 set+变量首字母大写可以直接使用: dispatch({type: 'setName', payload: '新的名称'})的方式修改变量值。

// 修改变量“name”的值
dispatch({
  type: 'setName',
  payload: 'Jack',
});
直接修改状态

如果你不想用 dispatch,你也可以采用更简便的方式修改,如下:

// 声明变量
const [variables] =
  useVariable <
  DataType >
  {
    variables: {
      name: '',
      userInfo: [],
    },
  };

// 修改变量
variables.name = 'Jack';

useVariable 内部使用 proxy 监听 variable 对象属性的变动,会自动调用对应的 setXXX reducer 修改值。

注意 ⚠️: 你必须直接使用 “variables.属性” 的方式修改,如果你对 variables 进行了解构,将不会响应式更新数据

自定义 reducer

useVariable 会自动生成 setXXX 的 reducer,但是如果你需要对数据进行一些复杂操作,其也支持自定义 reducer 的方式,可以在 config 对象内部直接定义 reducers 属性,其接受一个对象,key 为 reducer 名称,value 为 reducer 函数。reducer 函数定义如下:

/** 存储对象 */
export type StoreObject = Record<Type, any>;

/** 类型 */
export type Type = string;

/** action对象 */
export type Action = {
  type?: Type,
  payload?: any,
};

/** reducer函数 */
export type Reducer = (store: StoreObject, action: Action) => StoreObject;

reducer 函数在调用的过程中,会传入当前的存储数据 store,以及当前 dispatch 的 action,你可以直接使用 store[属性名] = action.paylaod 的方式 如:

// 需要手动修改姓名,并且在姓名后面自动加入 "(已修改)"字符串
reducers: {
  customChangeName: (store,action){
    store.name = action.payload + "(已修改)"
  }
}

// 使用dispatch修改name
dispatch({
  type: 'customChangeName',
  payload: 'Jack'
})

即可将姓名修改成 "Jack(已修改)"

处理副作用

useVariable 内部采用代数效应思想,将异步请求部分抽离出去,你可以忘掉 async await! 直接假设 call 函数返回的就是需要的业务数据即可!

  // 声明副作用
  effects: {
    // 获取用户信息
    fetchuserInfo: ({ call, setLoading }, { store }, payload) => {
      setLoading(true)
      const { success, data } = call(queryUserInfoById, payload);
      const {name,infoList} = success ? data : [];
      store.name = name
      store.infoList = infoList
      // 关闭loading
      setLoading(false);
    },
  },

我们定义 fetchuserInfo 函数,这个函数的第一个参数内 包含函数 call 和 setLoading,其中 call 函数用来把异步函数转换成同步写法,其定义如下:

type Call = (asyncFunc: (...args: any[]) => Promise<any>, payload?: any) => any;

第一个函数为一个返回 Promise 的异步函数,但是在这里你不需要用 await 等待,call 方法会把后面传入的参数一次传递给这个异步函数,并且直接返回请求结果,你只需要假设 call 方法直接返回业务数据即可!

这样设计有效避免了 async await 传染问题,在 effect 函数内部,你可以完全忽略数据的请求过程,专注业务逻辑,做到业务和实现的结偶。

具体实现原理可以见 /hooks/useStore/libs/task 采用抛出异常的方式暂停函数运行并且等待请求结果,在获得结果后缓存并且重新运行函数。

注意 ⚠️,effect 函数需要为纯函数,请不要在其内部加入 console 输出或者修改外部作用域等操作。