【发布时间】:2019-12-14 14:19:46
【问题描述】:
我有一个组件多次使用相同的 React 钩子,使用传递给钩子内部组件的道具。对于这个例子,我使用了 useCallback 钩子和 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>
);
};
我想通过定义我自己的自定义钩子来稍微干燥一下。我的第一次尝试直接在组件内部定义了钩子:
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——这个依赖数组应该是
[action, finish]。这是我犯的一个错误,因为在创建此示例时我没有启用 linting 规则。我理解正确指定这些的重要性。
这似乎可行,但examples for creating custom hooks 都将钩子放在文件的顶层。钩子常见问题解答are hooks slow because of creating functions in render? 解决了创建函数的性能,但示例是传递到钩子的函数。
在组件内创建一个钩子是否有任何功能问题需要注意?如果这通常是可以接受的,那么在钩子中捕获组件的属性是否有任何具体问题?
一些答案质疑为什么我不想将钩子放在组件之外。主要原因是我希望避免这种解决方案的重复和冗长,如以下快速演示所示:
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 结合使用。动作创建者是相当静态的,因为它们是被导入的:
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
}
将此与渲染函数外部的钩子进行对比,需要传入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
}
【问题讨论】:
-
我猜
[action, props, dispatch]也不正确:道具本身每次都是一本新字典。所以它不会工作。应该是[action, close, dispatch]
标签: javascript reactjs closures react-hooks