【问题标题】:React functional component reinitialises local functions and variables on re render (React Hooks)React 功能组件在重新渲染时重新初始化本地函数和变量(React Hooks)
【发布时间】:2018-11-06 17:02:55
【问题描述】:

所以我现在已经开始使用 React 钩子了。我已经用 API 试验了一段时间了。我真的很喜欢将状态带入功能组件的想法。但是有一件事一直困扰着我,当我尝试使用它时,它感觉不对劲。我尝试在RFCs 上发帖,但现在那里太拥挤了。一切似乎都消失了。

这是我的示例中的一段代码。

import React, { useState } from "react";

function Counter() {
  const [counterState,incrementCounterState] =  useCommontState(0); 

  function doSomething (){
   // does something and then calls incrementCounterState
   // with the updated state.
  }

   return (
    <div>
      <p>{counterState}</p>
      <button onClick={incrementCounterState}>increase</button>
      ....
      .... // some jsx calling local scoped functions.
      ....
    </div>
    );
}

function useCommontState(defaultValue){
  var [state, setState] = useState(0);
  function increment(){
    setState(defaultValue+=1);
  }
  return [state, increment]
}

export default Counter;

我可以轻松取出 state 和 setState 方法并创建自定义 hook 但我的问题在于组件使用的本地函数。由于状态现在是组件的一部分,因此在某些情况下,某些逻辑将决定下一步如何处理状态。

此外,当组件在状态更改时重新渲染时,所有内容都会重新初始化。这是我的问题。我知道useState 有自己的方式来处理这个问题。但我的问题在于我自己的功能。点击处理程序。在更改事件、子组件的回调等上,所有这些都将在每次组件呈现时重新初始化。我觉得这不对。

有什么方法可以解决这个问题。这是一个新的 API。我们甚至不确定它是否会变成react 17。但是有没有人遇到过更好的方法呢?

【问题讨论】:

  • 不确定我是否理解您的担忧,但我之前玩过一次,最后用useEffect(() =&gt; doSomething(), [false]) 做一次初始化工作,有点像componentDidMount - 请参阅reactjs.org/docs/…
  • 我猜人们的反应是,功能重新初始化的成本与它解决的其他问题相比微不足道。

标签: javascript reactjs react-hooks


【解决方案1】:

当我第一次看到该提案时,我也有同样的担忧,但React Docs Hooks Proposal FAQ:

Hooks 会因为在渲染中创建函数而变慢吗?

没有。在现代浏览器中,与类相比,闭包的原始性能没有显着差异,除非在极端情况下。

我的收获是,虽然您现在在每次渲染的重复声明中有额外的开销,但您在其他地方有额外的胜利:

  • Hooks 避免了类所需的大量开销,例如在构造函数中创建类实例和绑定事件处理程序的成本。

  • 使用 Hooks 的惯用代码不需要在使用高阶组件、渲染道具和上下文的代码库中普遍存在的深层组件树嵌套。使用更小的组件树,React 要做的工作更少。

总体而言,好处可能多于使钩子值得使用的缺点。

【讨论】:

  • 感谢您指出来源。我在那里找到了很好的资源。 React 团队坚信,在现代浏览器中,闭包的开销并不是什么大问题。
【解决方案2】:

您始终可以通过将所需值作为常量传递来简化代码以取出函数,以便它们不会始终被初始化。

    import React, { useState } from "react";

    function doSomething (counterState, incrementCounterState){
       // does something and then calls incrementCounterState
       // with the updated state.
   }
    function Counter() {
      const [counterState,incrementCounterState] =  useCommontState(0); 

       return (
        <div>
          <p>{counterState}</p>
          <button onClick={incrementCounterState}>increase</button>
          ....
          .... // some jsx calling local scoped functions.
          ....
        </div>
        );
    }

    function increment(defaultValue, setState){
        setState(defaultValue + 1);
    }

    function useCommontState(defaultValue){
      var [state, setState] = useState(0);
      return [state, increment]
    }

    export default Counter;

在我看来,所有演示和文档中建议的功能设计也是为了让人们熟悉它,然后考虑重新初始化方面。此外,重新初始化的成本将大大超过它提供的其他好处。

【讨论】:

  • 我正在阅读有关钩子的反应文档,我发现使用诸如 useMemo、useCallback 等反应钩子来重新初始化函数问题会更好。如果我遵循您的模式,那么我需要在我的渲染中创建一个闭包,以使用我真的不想使用的参数调用这些函数
【解决方案3】:

我正在使用createOnce 辅助函数来防止重新初始化,但我不确定它是否正确。

utils/createOnce.js

import { useMemo } from 'react';

export const createOnce = toCreate => useMemo(() => toCreate, []);

SomeComponent.js

...

const someFunction = createOnce((counter) => {
  // whatever
  return counter + 1;
});

...

【讨论】:

    猜你喜欢
    • 2019-10-28
    • 2019-07-28
    • 1970-01-01
    • 2020-07-07
    • 2020-02-05
    • 2020-07-29
    • 1970-01-01
    • 2020-01-06
    • 2022-11-29
    相关资源
    最近更新 更多