我有一个组件多次使用相同的 React hook,使用传递给 hook 内部组件的 prop。对于这个例子,我使用 useCallback
钩子(Hook)和 finish
属性,这只是另一个要调用的函数:
const Example1 = ({ finish }) => {
const runA = useCallback(
() => {
console.log("running A");
finish();
},
[finish]
);
const runB = useCallback(
() => {
console.log("running B");
finish();
},
[finish]
);
return (
<Fragment>
<button onClick={runA}>A</button>
<button onClick={runB}>B</button>
</Fragment>
);
};
我想通过定义我自己的自定义 Hook 来稍微整理一下。我的第一次尝试直接在组件内部定义了钩子(Hook):
const Example2 = ({ finish }) => {
const useCustomHook = action =>
useCallback(
() => {
action();
finish();
},
[finish] // See note 1 below
);
const runA = useCustomHook(() => console.log("running A"));
const runB = useCustomHook(() => console.log("running B"));
return (
<Fragment>
<button onClick={runA}>A</button>
<button onClick={runB}>B</button>
</Fragment>
);
};
1 — this dependency array should be
[action, finish]
. It's a mistake I made because I didn't have linting rules enabled when creating this example. I understand the importance of correctly specifying these.
这似乎有效,但是 examples for creating custom hooks都将钩子(Hook)放在文件的顶层。钩子(Hook)FAQ are hooks slow because of creating functions in render?解决了创建函数的性能问题,但示例是传递给 钩子(Hook)的函数。
在组件内部创建钩子(Hook)时是否有任何需要注意的功能问题?如果这通常是可以接受的,那么在钩子(Hook)中捕获组件的属性是否存在任何特定问题?
有些回答质疑为什么我不想把钩子(Hook)放在组件外面。主要原因是因为我希望避免这种解决方案的重复和冗长,如这个快速演示所示:
const useTopLevelCustomHook = ({ finish, action }) =>
useCallback(
() => {
action();
finish();
},
[action, finish]
);
const Example3 = ({ finish }) => {
const runA = useTopLevelCustomHook({
action: () => console.log("running A"),
finish
});
const runB = useTopLevelCustomHook({
action: () => console.log("running B"),
finish
});
return (
<Fragment>
<button onClick={runA}>A</button>
<button onClick={runB}>B</button>
</Fragment>
);
};
一些答案侧重于在组件内创建闭包,这不是我的本意。在我最初的 TypeScript 应用程序中,我将 Redux 操作与传入的 prop 结合使用。 Action 创建者是相当静态的,因为它们是导入的:
import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import * as actions from './actions';
interface BuildMenuProps {
close: () => void;
}
const BuildMenu: React.SFC<BuildMenuProps> = props => {
const dispatch = useDispatch();
const useDispatchAndClose = (action: () => void) => useCallback(
() => {
dispatch(action());
props.close();
},
[action, props, dispatch]
);
const compile = useDispatchAndClose(actions.performCompile);
const compileToAssembly = useDispatchAndClose(actions.performCompileToAssembly);
const compileToLLVM = useDispatchAndClose(actions.performCompileToLLVM);
const compileToMir = useDispatchAndClose(actions.performCompileToMir);
const compileToWasm = useDispatchAndClose(actions.performCompileToNightlyWasm);
const execute = useDispatchAndClose(actions.performExecute);
const test = useDispatchAndClose(actions.performTest);
// JSX that uses these callbacks
}
将此与渲染函数外部的钩子(Hook)进行对比,后者需要传入 props.close
:
const useDispatchAndClose = (action: () => void, close: () => void) => {
const dispatch = useDispatch();
return useCallback(
() => {
dispatch(action());
close();
},
[action, close, dispatch]
);
}
const BuildMenu: React.SFC<BuildMenuProps> = props => {
const compile = useDispatchAndClose(actions.performCompile, props.close);
const compileToAssembly = useDispatchAndClose(actions.performCompileToAssembly, props.close);
const compileToLLVM = useDispatchAndClose(actions.performCompileToLLVM, props.close);
const compileToMir = useDispatchAndClose(actions.performCompileToMir, props.close);
const compileToWasm = useDispatchAndClose(actions.performCompileToNightlyWasm, props.close);
const execute = useDispatchAndClose(actions.performExecute, props.close);
const test = useDispatchAndClose(actions.performTest, props.close);
// JSX that uses these callbacks
}
最佳答案
我认为您的方法没有问题。所有的钩子(Hook)仍然在每次渲染时以相同的顺序无条件地被调用。 React 无法在运行时区分 Example1
与 Example2
中的代码结构如何与 React API 交互。
This appears to work, but the examples for creating custom hooks all place the hook at the top-level of the file.
文档中的自定义 Hook 示例展示了如何创建可在多个组件中重复使用的 Hook 。为此,他们需要处于顶层。您有不同的用例。
至于这在技术上是否是“自定义 Hook ”? React Hooks ESLint 规则明确将其视为自定义钩子(Hook),否则您会收到关于在“既不是 React 函数组件也不是自定义 React Hook 函数”的函数中调用钩子(Hook)的警告(例如,如果您重命名 useCustomHook
到 usingCustomHook
你会得到一个警告)。 ESLint 规则将强制执行 rules of hooks在您的自定义 Hook 上。 Here is a sandbox我在 useCallback
调用中故意遗漏了一个依赖项,ESLint 规则报告了缺少的依赖项('greetee')。
我唯一的警告是在 documentation它确实说:
Don’t call Hooks inside loops, conditions, or nested functions.
您的自定义 Hook 可以被视为嵌套函数。如果这种定义自定义 Hook 的方式在未来由于我没有看到的原因而被确定为有问题,您可能会面临 ESLint 规则稍后以提示此方式更改/增强的风险。
Are there any functional issues to be aware of with creating a hook inside of a component?
我看不出这种使用方式与在顶层定义 Hook 并向其传递额外参数的等效代码有任何不同。
If that's acceptable in general, are there any specific issues with capturing a property of the component in the hook?
自定义钩子(Hook)将在每次渲染时重新定义,因此它不会以任何可能改变可观察行为的有意义的方式“捕获”组件的属性,只要您只在它所在的同一组件中无条件地调用它被定义。如果您改为将自定义 Hook 作为属性传递给另一个组件,然后调用它,我认为这可能会有问题(这将是一种有问题的使用模式,可能会导致 ESLint 规则被收紧以提示你在做什么)。
关于javascript - 在组件内部定义自定义 Hook 有什么问题吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57386035/