【问题标题】:Correct way to create event handlers using hooks in React?在 React 中使用钩子创建事件处理程序的正确方法?
【发布时间】:2019-07-26 20:03:25
【问题描述】:

在典型的基于类的 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 显示这两种方法。正如您从那里的代码中看到的那样,似乎都没有在每次渲染时不必要地重新创建事件处理程序,因此我认为不可能存在性能问题。

【问题讨论】:

  • 您不需要使用钩子或经典组件在组件内创建函数。您仍然可以在功能组件的主体内部或外部声明 const 函数。

标签: javascript reactjs jsx react-hooks


【解决方案1】:

我不会推荐useStateuseRef

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

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

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

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

  1. 作为一种性能优化,可避免声明不必要的函数。
  2. 为了避免不必要的纯组件重新渲染。

我不太担心第一点:钩子会在函数内部声明函数,而且成本不太可能成为影响应用性能的主要因素。

但是第二点有时是有效的:如果一个组件被优化(例如使用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 中,它会起作用...但在大多数情况下,它没有任何帮助:您使用 &lt;button&gt; 的原始示例不会从记忆回调中受益,因为 &lt;button&gt; 是'不是一个优化的组件。

【讨论】:

  • your original example with &lt;button&gt; won't benefit from a memoized callback, since &lt;button&gt; isn't an optimized component. 你这是什么意思?为什么它没有帮助? DOM 的 "onClick" 属性不会在每次渲染时无意识地更新,这不是我们想要的吗?
  • @KostiantynKolesnichenko 这些优化的要点是避免组件 render 逻辑的开销,并且没有用于 DOM 元素的渲染函数 - 它本质上只是生成一个静态对象表示元素。 React 需要更新 DOM,但翻转事件处理程序的成本几乎可以肯定是微不足道的。但是因为函数实例发生变化而重新渲染整个新组件的成本可能是不必要的。
  • reconciler 替换事件处理程序时是否真的触及 DOM?
  • @thorn̈ 是的,我相信答案是否定的; React 使用合成事件并将它们委托给您的自定义处理函数。
猜你喜欢
  • 2021-09-12
  • 1970-01-01
  • 1970-01-01
  • 2016-02-03
  • 2020-02-17
  • 2021-04-07
  • 1970-01-01
  • 2016-05-26
  • 2013-01-04
相关资源
最近更新 更多