gpt4 book ai didi

reactjs - React hook 中的 Object.is 相等性检查导致同一函数有多个版本

转载 作者:行者123 更新时间:2023-12-03 13:35:17 25 4
gpt4 key购买 nike

我不确定停止以下情况的正确解决方法是什么。我创建了this codesandbox突出问题。

我有这个钩子(Hook),这是一个缩小版:

export const useAbortable = <T, R, N>(
fn: () => Generator<Promise<T>, R, N>,
options: Partial<UseAbortableOptions<N>> = {}
) => {
const resolvedOptions = {
...DefaultAbortableOptions,
...options
} as UseAbortableOptions<N>;
const { initialData, onAbort } = resolvedOptions;
const initialState = initialStateCreator<N>(initialData);
const abortController = useRef<AbortController>(new AbortController());
const counter = useRef(0);

const [state, dispatch] = useReducer(reducer, initialState);

const runnable = useMemo(
() =>
makeRunnable({
fn,
options: { ...resolvedOptions, controller: abortController.current }
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[counter.current]
);

const runner = useCallback(
(...args: UnknownArgs) => {
console.log(counter.current);

dispatch(loading);

runnable(...args)
.then(result => {
dispatch(success<N>(result));
})
.finally(() => {
console.log("heree");
counter.current++;
});
},
[runnable]
);

return runner;
};

钩子(Hook)接受一个函数和选项对象,并且当它们在每次渲染时重新创建时,并且钩子(Hook)使用Object.is比较,它正在创建返回函数的新版本,无论我做什么做。

所以我像这样破解了它,使用计数器:

  const runnable = useMemo(
() =>
makeRunnable({
fn,
options: { ...resolvedOptions, controller: abortController.current }
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[counter.current]
);

我必须让 linter 保持沉默才能使这成为可能。

linter 的建议是这样的:

  const runnable = useMemo(
() => makeRunnable({ fn, options: { ...resolvedOptions, controller: abortController.current } }),
[fn, resolvedOptions],
);

但是 fnresolvedOptions 会导致每次创建一个新的可运行函数。

必须将所有内容包装在 useCallbackuseMemo 和 friend 中,这确实很痛苦。

我查看了其他获取库,他们正在做其他类似的事情,例如 JSON.stringify 依赖项数组来解决这个问题。

我喜欢钩子(Hook),但 Object.is 相等性检查正在扼杀整个范式。

正确使用依赖项数组的正确方法是什么,这样我就不会每次都得到一个新函数,同时也能让 linter 满意?这两个要求似乎互相增加了可能性。

最佳答案

您必须注意,您不需要提供任何 hack 来解决回调问题

关于缺少依赖项的 ESLint 警告是为了帮助用户避免在不知不觉中犯下的错误,并且不会被强制执行。

现在就你的情况

如果你看看你的useAbortable函数,您传递的是 generator functionan options object ,现在它们都是在每次重新渲染时创建的。

您可以memoize the optionsfunction传递至useAbortable避免依赖问题

  • 如果您使用callback pattern for setMessages ,您可以创建onAbort只需提供 [] 一次对 useCallback 的依赖

  • 生成器函数取决于状态的时间延迟,因此您可以使用 useCallback 创建它并提供 delay作为依赖项

      const onAbort = useCallback(() => {
setMessages(prevMessage => (["We have aborted", ...prevMessage]));
}, []); //


const generator = useCallback(
function*() {
const outsideLoop = yield makeFetchRequest(delay, "outside");

processResult(outsideLoop);

try {
for (const request of requests) {
const result = yield makeFetchRequest(delay, `${request.toString()}`);

processResult(result);
}
} catch (err) {
if (err instanceof AbortError) {
setMessages(["Aborted"]);
return;
}
setMessages(["oh no we received an error", err.message]);
}
},
[delay, processResult]
);

const options = useMemo(() => ({ onAbort }), [onAbort]);
const { run, state, abortController, reset, counter, ...rest } = useAbortable<
Expected,
void,
Expected
>(generator, options);

现在里面useAbortable你不用担心fnoptions改变,因为只有当它们绝对改变时,如果我们像上面那样实现它,它们才会改变

因此,您可以使用正确的依赖项清楚地创建 useAbortable 中的可运行实例

const resolvedOptions = useMemo(() => ({
...DefaultAbortableOptions,
...options
}), [options]);

const runnable = useMemo(
() =>
makeRunnable({
fn,
options: { ...resolvedOptions, controller: abortController.current }
}),
[fn, resolvedOptions]
);

Working demo

关于reactjs - React hook 中的 Object.is 相等性检查导致同一函数有多个版本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61715995/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com