最近项目不停迭代,工作一直没停,遇到大大小小的问题,还好都慢慢解决了。好久都没有好好记录问题了,拾起来。
在开发中遇到一个需求,多个接口循环调用,并且接口调用根据用户的不同操作调用方式不同,但是最后都要将所有获取的数据存下来更新视图。例如下面这种:
我是栗子
const [result, setResult] = useState({});
const fetchList = () => {
serviceList.map(async (item) => {
const response = await item.service();
// 在每次获取到数据后setState,存到状态中
setResult({ ...result, [item.key]: response });
});
};
复制代码
上述只是简单举例,真实场景一般都会比这个复杂。乍一看这样并没什么不妥,但是问题就是,一旦执行完上述fetch方法后,拿到的result状态完全不是所有结果的集合,而是最后一次的更新...
第一次失败尝试
这样就很难办了,因为本身react的setState钩子是继承了之前的setState方法,所以自然是异步的,目的是提高性能,避免不必要的更新。
聪明的搬砖工怎么会被这种小土堆难住,所以想到一个办法就是先在循环外部定义一个临时变量存储所有的结果,然后统一赋值给result,于是就有了下面的版本:
const [result, setResult] = useState({});
const fetchList = () => {
let nowResult;
serviceList.map(async (item) => {
const response = await item.service();
// 获取数据后添加到临时变量中更新
nowResult = { ...nowResult, [item.key]: response };
// 记得要更新state,因为这里是异步调用接口,在外面会造成拿不到结果
setResult({ ...result, ...nowResult });
});
};
复制代码
useReducer成功解决
问题解决了,但是随后又发现一个问题,fetch可能在不同的情况下调用很多次...所以在第一次调用完的结果还没有来的及更新到result,第二次又调用,依次进行,这样就回到了原点。但是这样也不能挡住我对技术的热情(烦躁),在不断google的情况下,我终于发现了useReducer的妙用,原来它不仅仅可以适用于复杂的状态更新,还可以同步更新状态,赞!所以有了下面的版本:
const [result, setResult] = useReducer((state, action) => {
switch (action) {
//这里是一个投机的写法,无论action是什么,都按照下面的形式更新state
default:
return { ...state, ...action };
}
}, {});
const fetchList = () => {
serviceList.map(async (item) => {
const response = await item.service();
// 记得要更新state,因为这里是异步调用接口,在外面会造成拿不到结果
setResult({ ...result, [item.key]: response });
});
};
复制代码
这样临时变量也不用了,世界变得美好了(~ ̄▽ ̄)~
对待真爱就要多了解:useReducer
简单介绍下useReducer,其实这个钩子我不是很常用,这个钩子适用于复杂变量的更新,
const [result, setResult] = useReducer((state, action) => {
switch (action) {
case 'action1':
return {...state, result1};//具体更新方法
case 'action2':
return {...state, result2};//具体更新方法
default:
return { ...state, ...action };
}
}, initialState);
复制代码
这个钩子声明变量形式和useState一致,不过useReducer接收的第一个函数以state和action为参数,内部跟之前redux定义reducer方法形式一致,第二个参数为原始的状态变量,是不是很好用。
有问题欢迎指出,觉得有用的给我个大拇指吧o( ̄▽ ̄)o
生活的一部分是工作,工作的一部分是解决问题取悦生活,所以好好生活,好好工作,好好热爱(●ˇ∀ˇ●)