在典型的基于类的React组件中,这就是我创建事件处理程序的方式:
class MyComponent extends Component {
handleClick = () => {
...
}
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}
但是,当我使用基于钩子的功能范例时,我发现自己有两个选择:
const MyComponent = () => {
const [handleClick] = useState(() => () => {
...
});
return <button onClick={handleClick}>Click Me</button>;
};
或者:
const MyComponent = () => {
const handleClick = useRef(() => {
...
});
return <button onClick={handleClick.current}>Click Me</button>;
};
客观上哪个更好,出于什么原因?还有我还没有听说过的(更好)方式吗?
谢谢您的帮助。
编辑:我已经举了一个展示两种方法的示例here on CodeSandbox。正如您从那里的代码可以看到的那样,似乎都没有必要在每个渲染器上重新创建事件处理程序,所以我认为不可能存在性能问题。
答案 0 :(得分:9)
我不建议使用useState
或useRef
。
实际上您根本不需要任何钩子。在许多情况下,我建议您简单地这样做:
const MyComponent = () => {
const handleClick = (e) => {
//...
}
return <button onClick={handleClick}>Click Me</button>;
};
但是,有时建议避免在渲染函数中声明函数(例如jsx-no-lambda
tslint规则)。这有两个原因:
对于第一点,我不会太担心:钩子将在函数内部声明函数,并且这种花费不太可能成为影响应用性能的主要因素。
但是第二点有时是有效的:如果某个组件已经过优化(例如,使用React.memo
或被定义为PureComponent
),以便仅在提供新道具时才重新渲染,并通过新的功能实例可能会导致组件不必要地重新呈现。
为处理此问题,React提供了useCallback
钩子,用于记录回调:
const MyComponent = () => {
const handleClick = useCallback((e) => {
//...
}, [/* deps */])
return <OptimizedButtonComponent onClick={handleClick}>Click Me</button>;
};
useCallback
仅在必要时(每当deps数组中的值发生变化时)返回一个新函数,因此OptimizedButtonComponent
不会重新呈现超出必要的内容。因此,这解决了问题2。 (请注意,它不能解决问题#1,每次渲染时,仍然会创建一个新函数并将其传递给useCallback
)
但是我只会在必要时这样做。您可以将每个回调都包装在useCallback
中,并且可以正常工作……但是在大多数情况下,它没有任何帮助:您最初使用<button>
的示例将不会从记忆化的回调中受益,因为<button>
不是经过优化的组件。