如何等多个useState更改状态之后再去执行useEffect?

我现在这种写法每次setXX都要执行getData,怎样让他把3个set都执行完了再去getData

  const [name, setName] = useState('')
  const [mobile, setMobile] = useState('')
  const [tag, setTag] = useState('')
  const onSearch = (values) => {
    setName(values.name)
    setMobile(values.mobile)
    setTag(values.tag_id)
  }

  useEffect(() => {
    getData()
  }, [name, mobile, tag])
阅读 2.6k
3 个回答

方法1:不使用useEffect来触发接口获取新数据,将getData绑定到按钮事件上,但是这样开发者需要关注更多操作。

useEffect(() => {
    getData()
}, [])

// 查询
const onSearch = () => {
    // getData 获取方法中需要做更多的状态维护
    getData()
}

方法2:将多个状态合并,依赖就变成一个。但是如果关联性不是太强,合并在一起会增加维护成本

const [searchParams, setSearchParams] = useState({
    name: '',
    mobile: '',
    tag: ''
})

useEffect(() => {
    getData()
}, [searchParams])

方法3(较为推荐):有多个依赖项同时更改时, 回调函数只执行最新的一次,使用防抖避免短时间多长触发

// useDebounce.ts
import { useRef } from 'react';
import { debounce } from 'lodash-es';

export function useDebounce<T extends Function>(fn: T, wait = 1000) {
  const func = useRef(fn);
  func.current = fn
  const debounceWrapper = useRef(debounce((args) => func.current?.(args), wait));
  return (debounceWrapper.current as unknown) as T;
}

// 业务文件内
import { useDebounce } from '@/hooks/useDebounce'


const getData = useDebounce(() => {
    // 获取最新数据
}, 0)

useEffect(() => {
    getData()
}, [name, mobile, tag])

那如果我们的useEffect中有多个函数需要执行,是不是就要对每个函数加上防抖了,无疑有点重复,那进一步优化代码,将函数改为可以支持批量更新操作,在原来基础上进行封装

/**
 * 批量设置更新
 */
export function useBatchEffect(effect: EffectCallback, deps?: DependencyList, wait = 0) {
  const fn = useDebounce(effect, wait);
  useEffect(fn, deps);
}

// 业务文件内
import { useBatchEffect } from '@/hooks/useDebounce'

const getData = () => {
    // 获取最新数据
}

// 批量操作
useBatchEffect(() => {
    getData();
}, [type, status]);

以上就是主要的一些代码实现部分,需要你将其自己梳理好整理业务逻辑。希望对你有帮助

楼上的方法二具有可操作性,但是每次都写确实很麻烦,好在可以自行封装 Hook ,把依赖项封装一下就行了:

export const useUnionEffect = (callback, deps) => {
  const ref = useRef([]);
  const {current: lastDeps} = ref;
  // 此处使用全等比较,与 react 的比对方法不一样,也可以参考 react 的实现
  if(lastDeps.every((dep, idx) => dep !== deps[idx])){
    ref.current = deps;
    callback(...deps);
  }
}

使用方法比 useEffect 麻烦一点,callback 中依赖的外部参数不能直接从组件作用域中取,不然会因为捕获到原来的变量导致“闭包陷阱”:

useUnionEffect((name, mobile, tag) => {
  getData();
}, [name, mobile, tag])

或者也可以使用 useCallback 追踪依赖再放到这个自定义 hook 函数的函数内调用,有更符合直觉的解决方法的话也可以在此基础上改进。

加一个state (true/false), 代表三个set都执行完了?然后在getData之前判断state,如果为true,执行getData,getData完了把state设置为false?

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏