React自定义Hook获取数据返回旧值:如何让resolveOptions获正确结果?
问题分析
原代码的核心问题是:
data是一个一次性创建的Promise,它会立即用当前的dataRef.current值resolve,后续dataRef更新时,这个Promise不会重新生成,所以resolveOptions里拿到的永远是旧值。getData仅触发state更新和服务器请求,但无法让resolveOptions等待这次异步请求的结果返回。
修改方案
我们需要让每次调用getData时,都能获取到对应搜索请求的最新结果,而非复用旧的Promise。可以通过以下步骤修改:
1. 重构usePromise钩子,让getData返回Promise
把usePromise改成每次调用getData时,返回一个等待当前搜索结果的Promise,同时用ref跟踪当前活跃的请求,避免旧请求干扰:
export const MyComponent = () => { const { getData } = usePromise(); const usePromise = () => { const [searchValue, setSearchValue] = useState<string>(); // 用ref跟踪当前请求的resolve函数 const resolveRef = useRef<(value: string[]) => void>(); const { results } = useHookToFetchDataFromServer({ filter: { field: searchValue } }); useEffect(() => { if (results && resolveRef.current) { const items = results.items.map(item => item.label); // 触发当前请求的Promise resolve resolveRef.current(items); // 清空ref,避免重复触发 resolveRef.current = undefined; } }, [results]); const getData = (searchVal: string): Promise<string[]> => { setSearchValue(searchVal); // 返回一个新的Promise,把resolve函数存在ref里 return new Promise((resolve) => { resolveRef.current = resolve; }); }; return { getData }; }; const resolveOptions = async (search: string): Promise<string[]> => { // 直接等待getData返回的Promise,拿到当前搜索的最新结果 const res = await getData(search); console.log(res); // 现在会显示当前搜索的正确结果 return res; }; return (<MyChildComponent optionsResolver={resolveOptions} />); };
2. 关键修改点说明
- 移除了
dataRef和固定的dataPromise,改为每次调用getData时动态生成新的Promise,确保对应当前搜索请求。 - 用
resolveRef跟踪当前需要resolve的Promise回调,当服务器返回最新results时,触发回调返回结果。 resolveOptions改为异步函数,直接等待getData的返回值,确保拿到当前搜索的最新结果。
额外优化:处理竞态问题
如果用户快速连续搜索,可能出现旧请求比新请求晚返回的情况,可通过跟踪当前搜索值过滤旧结果:
const usePromise = () => { const [searchValue, setSearchValue] = useState<string>(); const resolveRef = useRef<(value: string[]) => void>(); // 跟踪当前正在等待的搜索值 const currentSearchRef = useRef<string>(); const { results } = useHookToFetchDataFromServer({ filter: { field: searchValue } }); useEffect(() => { if (results && resolveRef.current && currentSearchRef.current === searchValue) { const items = results.items.map(item => item.label); resolveRef.current(items); resolveRef.current = undefined; } }, [results, searchValue]); const getData = (searchVal: string): Promise<string[]> => { setSearchValue(searchVal); currentSearchRef.current = searchVal; return new Promise((resolve) => { resolveRef.current = resolve; }); }; return { getData }; };
这样就能确保只有当前搜索值对应的结果才会被resolve,避免竞态导致的旧结果覆盖新结果。
内容的提问来源于stack exchange,提问作者Vlad Suciu
相关产品推荐
相关产品推荐

