在典型的基于类的反应组件中,这就是我将创建事件处理程序的方式:
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>;
};
哪一个在客观上更好,出于什么原因?我尚未听说或发现另一种(更好的)方式吗?
谢谢您的帮助。
编辑:我已经在显示这两种方法的代码和框上放了一个示例。正如您从那里的代码中可以看到的那样,似乎都不是不必要地重新创建事件处理程序,因此我认为可能的性能问题是不可能的。
我不建议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>
不是优化的组件。