React挂钩中功能组件内部的功能-性能

时间:2019-07-31 11:33:46

标签: javascript reactjs react-hooks

需要在反应Hooks的功能组件中具有功能。

据我研究,很多人都说这是不好的做法 因为它会在我们每次调用re-render时创建嵌套/内部函数。 经过分析,

我发现我们可以在元素上使用onClick={handleClick.bind(null, props)}并将功能放置在功能组件之外。

示例:

const HelloWorld = () => {
  function handleClick = (event) => {
    console.log(event.target.value);
  }

  return() {
    <>
        <input type="text" onChange={handleClick}/>
    </>
  }
}

请告知是否还有其他方法。

谢谢。

7 个答案:

答案 0 :(得分:4)

您可以使用useCallback功能:

const HelloWorld = ({ dispatch }) => {
  const handleClick = useCallback((event) => {
    dispatch(() => {console.log(event.target.value)}0;
  })

  return() {
    <>
        <input type="name" onChange={handleClick}/>
    </>
  }
}
  

useCallback将返回回调的记忆版本,该回调仅   如果依赖项之一已更改,则更改。这在以下情况下很有用   将回调传递给依赖引用的优化子组件   相等以防止不必要的渲染(例如shouldComponentUpdate)。

有关更多详细信息,请访问其参考链接:React useCallback


旧方法也有两个选择。

第一个解决方案: 将您的handleClick函数传递给功能组件。

const HelloWorld = (props) => {

  return() {
    <>
        <input type="name" onChange={props.handleClick}/>
    </>
  }
}

第二个解决方案: 在功能组件之外定义功能。

答案 1 :(得分:4)

不用担心

不用担心在每个渲染器上创建新功能。仅在极少数情况下会妨碍您的表现。 设置onClick处理函数不是其中之一,因此只需在每个渲染器上创建一个新函数。

但是,当需要确保每次都使用相同的功能时,可以使用useCallaback

为什么不为useCallback使用onClick

这就是为什么您不应该为useCallback处理程序(和大多数其他事件处理程序)而烦恼onClick的原因。

考虑以下代码片段,其中一个没有useCallback:

function Comp(props) {
  return <button onClick={() => console.log("clicked", props.foo)}>Text</Button>
}

和一个带有useCallback的

function Comp(props) {
  const onClick = useCallback(() => {
    console.log("clicked", props.foo)
  }, [props.foo])

  return <button onClick={onClick}>Text</Button>
}

后者的唯一区别是React doen拥有 如果onClick保持不变,则更改按钮上的props.foo更改回调是一项非常便宜的操作,而且完全没有 值得在代码上进行理论上的改进以使代码复杂化。

此外,值得注意的是每个渲染器上仍会创建一个新功能 即使您使用useCallback,但useCallback仍会返回旧的 只要作为第二个参数传递的依赖性不变。

为什么要使用useCallback

使用useCallback的要点是,如果您将两个函数与引用进行比较 相等,fn === fn2仅在fnfn2指向内存中的同一函数时才为真。 函数是否是否相同都没关系。

因此,如果您有备忘录或仅在功能更改时才运行代码, 再次使用useCallback可以使用相同的功能。

作为示例,React钩子使用probably比较旧依赖和新依赖,Object.is

另一个例子是React.PureComponent,它只会在道具或状态改变时重新渲染。这对于使用大量资源进行渲染的组件很有用。通过例如在每个渲染器上为PureComponent新建一个onClick都会使它每次都重新渲染。

答案 2 :(得分:3)

  

许多人都说这是一种不好的做法,因为每次我们称为重新渲染时,它都会创建嵌套/内部函数

不,内部函数/闭包非常普遍,它们没有问题。引擎可以极大地优化它们。

这里的意思是,您将函数作为道具传递给子组件。而且,由于该函数是“重新创建的”,所以它不等于传递的上一个函数,因此孩子确实会重新渲染(这对性能不利)。

您可以使用useCallback来解决该问题,它可以记住函数引用。

答案 3 :(得分:1)

enter image description here

有趣的问题,我和我的同事对此有一些担心,所以我进行了测试。

我创建了1个带有Hooks的组件和1个带有Class的组件,在其中放置了一些函数,然后将其渲染1000倍。

具有类的组件如下:

export class ComponentClass extends React.PureComponent {
  click1 = () => {
    return console.log("just a log");
  };

  render() {
    return (
      <>
        <span onClick={this.click1}>1</span>
      </>
    );
  }
}

带有挂钩的组件看起来像这样:

export const ComponentHook = React.memo((props) => {
  const click1 = () => {
    return console.log("just a log");
  };

  return (
    <>
      <span onClick={click1}>1</span>
    </>
  );
});

我向组件添加了更多单击处理程序,然后将它们渲染了约1000倍,由于类没有定义每个渲染的函数,因此Class更快,如果增加定义的函数数量,则差异会更大:

这里是一个codeandbox,因此您可以测试Class vs Hooks的性能:https://codesandbox.io/s/hooks-vs-class-forked-erdpb

答案 4 :(得分:1)

受到@tibbus 基准测试的启发,我制作了这个测试使用或不使用 useCallback 钩子的性能。经过多次执行,可以看出 useCallback 的使用对于高频渲染非常重要。

执行 1

enter image description here

执行 2

enter image description here

执行 3

enter image description here

https://codesandbox.io/s/usecallback-vs-raw-definition-xke9v?file=/src/App.js

答案 5 :(得分:0)

按照React Documentation(结尾部分)

后一种语法的问题是创建了不同的回调 每次LoggingButton呈现时。在大多数情况下,这很好。 但是,如果将此回调作为对较低组件的支持传递, 这些组件可能会进行额外的重新渲染。我们一般 建议在构造函数中绑定或使用类字段语法, 以避免这种性能问题。

类字段语法:

class LoggingButton extends React.Component {
  // This syntax ensures `this` is bound within handleClick.
  // Warning: this is *experimental* syntax.
  handleClick = () => {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}
回调语法中的

箭头函数:

class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // This syntax ensures `this` is bound within handleClick
    return (
      <button onClick={() => this.handleClick()}>
        Click me
      </button>
    );
  }
}

答案 6 :(得分:0)

老实说,在这些情况下,我只会使用类组件。我知道过早的优化,但每次创建一个新函数似乎都是一种奢侈的浪费,没有太多的可维护性优势。 tibbus 已经证明了 perf 命中,并且内联函数可以说比类方法可读性差。您失去的只是编写函数式组件的流畅感觉。