981377660LMT / ts

ts学习
6 stars 1 forks source link

react 闭包问题 #564

Open 981377660LMT opened 3 months ago

981377660LMT commented 3 months ago
// react闭包问题

/**
 * @param service 请求方法.
 * 请求的params需要在service内部作为状态处理.
 * 调用后,不会自动执行请求.
 */
function useService<T>(service: () => Promise<T>) {
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<Error>()
  const [data, setData] = useState<T>()

  const refresh = async (): Promise<void> => {
    try {
      setLoading(true)
      const data = await service()
      setLoading(false)
      setData(data)
    } catch (e) {
      setError(e as Error)
    } finally {
      setLoading(false)
    }
  }

  return {
    data,
    error,
    loading,
    refresh
  }
}

const [page, setPage] = useState<IPage>({
  pageNo: 1,
  pageSize: defaultPageSize
})

// 1.为什么没有闭包问题?
// !虽然service内部的闭包保存了page的值,
// !但是每次render时,service都会重新生成,page的值都是最新的
const { data, loading, refresh } = useService(() => getFlowVersionList({ page, id: flowId }))
useEffect(() => {
  refresh()
}, [page])

// 2.refresh 加上useCallback后,为什么有闭包问题?
// const newRefresh = useCallback(() => {
//   refresh();
// }, []);
// !refresh的闭包中保存了service,service的闭包保存了page的值,page的值不会变,所以会有闭包问题
981377660LMT commented 3 months ago

把闭包想象成一个隐藏的类,类就是作用域

981377660LMT commented 3 months ago

解决闭包问题: 本质是一个不变的函数引用了一个不变的值,因此两种方案:

  1. 让函数变化 -> 每次渲染都生成新的函数
  2. 函数不变,引用的值变化 -> 闭包内部用 ref 引用最新值/ 函数接受最新的参数
981377660LMT commented 3 months ago
// 解决方案1-1
const { data, loading, refresh } = useService(() => getFlowVersionList({ page, id: flowId }))

// 解决方案1-2
  const f = useCallback(() => {
    return getFlowVersionList({ page, id: flowId });
  }, [page, flowId]);
  const { data, loading, refresh } = useService(f);
981377660LMT commented 3 months ago
// 解决方案2-1
const pageRef = useRef(page)
const serviceRef = useRef(() => getFlowVersionList({ page: pageRef.current, id: flowId }))

// 解决方案2-2
const { data, loading, refresh } = useService(service)
fresh({ page, id: flowId })