javascript 在 React 中使用钩子创建事件处理程序的正确方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/55001372/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 10:29:22  来源:igfitidea点击:

Correct way to create event handlers using hooks in React?

javascriptreactjsjsxreact-hooks

提问by Lucas

In a typical class-based React component, this is how I would create an event handler:

在典型的基于类的 React 组件中,这是我创建事件处理程序的方式:

class MyComponent extends Component {
  handleClick = () => {
    ...
  }

  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}

However, I find myself with two options when I use a hooks-based functional paradigm:

但是,当我使用基于钩子的函数范式时,我发现自己有两个选择:

const MyComponent = () => {
  const [handleClick] = useState(() => () => {
    ...
  });

  return <button onClick={handleClick}>Click Me</button>;
};

or alternatively:

或者:

const MyComponent = () => {
  const handleClick = useRef(() => {
    ...
  });

  return <button onClick={handleClick.current}>Click Me</button>;
};

Which one is objectively better, and for what reason? Is there another (better) way that I have not yet heard of nor discovered?

客观上哪个更好,原因是什么?有没有我还没有听说过或发现的另一种(更好的)方法?

Thank you for your help.

谢谢您的帮助。

Edit: I have put an example here on CodeSandboxshowing both methods. Neither seems to unnecessarily recreate the event handler on each render, as you can see from the code on there, so a possible performance issue is out of the question, I think.

编辑:我在 CodeSandbox 上放了一个例子展示了这两种方法。正如您从那里的代码中看到的那样,两者似乎都没有不必要地在每次渲染时重新创建事件处理程序,因此我认为不可能出现性能问题。

回答by Retsam

I wouldn't recommend either useStateor useRef.

我不会推荐useStateuseRef

You don't actually need any hook here at all. In many cases, I'd recommend simply doing this:

你实际上根本不需要任何钩子。在许多情况下,我建议简单地这样做:

const MyComponent = () => {
  const handleClick = (e) => {
    //...
  }

  return <button onClick={handleClick}>Click Me</button>;
};


However, it's sometimes suggested to avoid declaring functions inside a render function (e.g. the jsx-no-lambdatslint rule). There's two reasons for this:

但是,有时建议避免在渲染函数中声明函数(例如jsx-no-lambdatslint 规则)。这有两个原因:

  1. As a performance optimization to avoid declaring unnecessary functions.
  2. To avoid unnecessary re-renders of pure components.
  1. 作为性能优化,以避免声明不必要的功能。
  2. 避免对纯组件进行不必要的重新渲染。

I wouldn't worry much about the first point: hooks are going to declare functions inside of functions, and it's not likely that that cost is going to be a major factor in your apps performance.

我不会太担心第一点:钩子将在函数内部声明函数,并且该成本不太可能成为您的应用程序性能的主要因素。

But the second point is sometimes valid: if a component is optimized (e.g. using React.memoor by being defined as a PureComponent) so that it only re-renders when provided new props, passing a new function instance may cause the component to re-render unnecessarily.

但第二点有时是有效的:如果一个组件被优化(例如使用React.memo或被定义为 a PureComponent)以便它只在提供新的 props 时重新渲染,传递一个新的函数实例可能会导致组件不必要地重新渲染。

To handle this, React provides the useCallbackhook, for memoizing callbacks:

为了解决这个问题,React 提供了useCallback钩子,用于记忆回调:

const MyComponent = () => {
    const handleClick = useCallback((e) => {
        //...
    }, [/* deps */])

    return <OptimizedButtonComponent onClick={handleClick}>Click Me</button>;
};

useCallbackwill only return a new function when necessary (whenever a value in the deps array changes), so OptimizedButtonComponentwon't re-render more than necessary. So this addresses issue #2. (Note that it doesn't address issue #1, every time we render, a new function is still created and passed to useCallback)

useCallback只会在必要时返回一个新函数(每当 deps 数组中的值发生变化时),因此OptimizedButtonComponent不会重新渲染超过必要的次数。所以这解决了问题#2。(请注意,它没有解决问题 #1,每次我们渲染时,仍然会创建一个新函数并将其传递给useCallback

But I'd only do this where necessary. You could wrap every callback in useCallback, and it would work... but in most cases, it doesn't help anything: your original example with <button>won't benefit from a memoized callback, since <button>isn't an optimized component.

但我只会在必要时这样做。您可以将每个回调都包装在 中useCallback,它会起作用……但在大多数情况下,它没有任何帮助:您的原始示例<button>不会从记忆化回调中受益,因为<button>它不是优化的组件。

回答by Masih Jahangiri

If your event function is small or medium (90% of cases):

如果您的事件函数很小或中等(90% 的情况):

import React from 'react';

const Component = () => {
  const handleEvent = (e) => { // React creates function whenever rendered
    // small or medium function
  }

  return <div onClick={handleEvent}>button</div>;
};

If your event function is large memoize it (10% of cases):

如果您的事件函数很大,请记住它(10% 的情况):

import React, { useCallback } from 'react';

const Component = () => {
  const handleEvent = useCallback((e) => {
    // large function
  }, [/* deps */]);

  return <div onClick={handleEvent}>button</div>;
};