useState与useReducer

useState

最常用的hook,使用方法不再赘述。

需要注意的是它还传一个函数,返回新的值,这个函数的参数是上一次的 state,这个就很有用了,如果你在使用它的过程中遇到了闭包,可以尝试这种写法更新state

举个我实际开发遇到的例子:

我在用户登录后通过事件总线注册了一个事件 downLoadCount,它的作用是将数据流里的downLoadCount加一.

js 复制代码
  let eventCownLoadCount = null;
  const { downLoadCount, setDownLoadCount } = useModel('header');
  useEffect(() => {
    getCount();
    if (eventCownLoadCount) {
      eventBus.un('addDownLoadCount', eventCownLoadCount);
    }
    eventCownLoadCount = eventBus.on('addDownLoadCount', function () {
      setDownLoadCount(downLoadCount+1);
    });
  }, []);

接着在其他页面点击按钮后触发这个事件

js 复制代码
 eventBus.emit('downLoadCount');

但结果是我的数据一直没有改变。于是是想到可能是有闭包

因为现在 useEffect 的依赖数组是 [],也就是只会执行并保留第一次的 function。而第一次的 function 引用了当时的 downLoadCount,形成了闭包

换成函数的写法就可以了

js 复制代码
eventCownLoadCount = eventBus.on('addDownLoadCount', function () {
      setDownLoadCount(downLoadCount=>downLoadCount++);
    });

useReducer

与useState类似都是用来修改数据的。解释起来比较抽象。与useState类比一下,基本写法是这样的。

js 复制代码
const [res, dispatch] = useReducer(reducer, { result: 0});

res,dispatch基本等同于state,useState,但是dispatch可以接受参数。那么正因为有这个参数,我们可以根据参数自由控制数据的修改逻辑。{ result: 0}相当于state的初始值。

可以看到useReducer还多了一个reducer函数,它的参数是res,dispatch接受的参数。现在我们可以把修改数据的逻辑封装在这个函数里面。

js 复制代码
function reducer(state, action) {
  switch (action.type) {
    case 'add':
      return {
        result: state.result + action.num,
      };
    case 'minus':
      return {
        result: state.result - action.num,
      };
  }
  return state;
}

修改数据调用dispatch,并传入参数,这样就可以进行数据的加减了

js 复制代码
<div onClick={() => dispatch({ type: 'add', num: 2 })}>加</div> 
<div onClick={() => dispatch({ type: 'minus', num: 1 })}>减</div>

看起来用useState都能实现,确实如此,这种适合state比较复杂,并且想要把修改的逻辑封装起来,所以它的可维护性更好。

并且一般搭配immer使用,因为在reducer中return 的值不能直接返回修改后的state。

补充:useReducer还有第三个参数,是一个函数用来延迟初始化状态

js 复制代码
const initialState = { count: 0 }; 
const init = (initialState) => { // 复杂的初始化逻辑 
      return { count: initialState.count + 10 }; 
};
const [state, dispatch] = useReducer(reducer, initialState, init);
//现在初始值就是{count:10}

好像多此一举了,但是如果这个初始值是根据接口拿到的,按照之前的方法就需要在一个函数fn里调用接口并return接口返回的值,也就是

js 复制代码
const [state, dispatch] = useReducer(reducer, fn());

这两种写法就有一个性能的差别,上面这种写法fn会在每次组件渲染时执行,初始逻辑写在第三个函数里只会在页面初次渲染执行一次

相关推荐
专注VB编程开发20年1 分钟前
C#异步状态机,内部的信号机制TaskCompletionSource
前端
csdn_aspnet4 分钟前
在无状态 ASP.NET Core 8 Web API 中实现 CSRF 令牌,无需 Views/MVC!
前端·csrf·.net core
M ? A29 分钟前
Vue转React最佳工具对比:Vuera、Veaury与VuReact
前端·javascript·vue.js·经验分享·react.js
We་ct35 分钟前
JS手撕:函数进阶 & 设计模式解析
开发语言·前端·javascript·设计模式·面试·前端框架
悟空瞎说1 小时前
前端老鸟实战:纯 CSS 实现小红书「真・瀑布流」,零 JS、自动错落、生产可用
前端
yuki_uix1 小时前
当 reduce 遇到二维数据:从"聚合直觉"到"复合 Map"的思维跃迁
前端·javascript·面试
我叫黑大帅1 小时前
Vue3中的computed 与 watch 的区别
前端·javascript·面试
暗不需求1 小时前
# 一文搞懂 JavaScript 内存机制:从栈和堆,到闭包为什么“活得更久”
前端·javascript
CharlesY1 小时前
JavaScript HTML5 Cache Manifest:离线应用缓存机制考古
前端·javascript
yuki_uix1 小时前
前端解题的 6 个思维模型:比记答案更有用的东西
前端·面试