如何在没有创建新函数的情况下将参数传递给React + Typescript中的回调?

时间:2017-07-19 17:10:28

标签: javascript reactjs typescript

假设您有以下TSX代码:

render() {
    const someParameter = 5;

    return (
        <div onClick={() => this.doSomething(someParameter)}></div>
    );
}

现在,我们知道我们不应该像这样将胖箭头函数传递给onClick处理程序,因为它每次都会创建一个新函数,强制重新呈现(这在以下情况下特别糟糕)我们将此回调传递给深层组件,而不仅仅是div。)

那么解决方案,如果没有参数,将在我们类的构造函数中绑定doSomethingthis,如this.doSomething = this.doSomething.bind(this),然后将绑定函数作为打回来。但是,在我们的情况下,这并没有削减它,因为我们想要将参数传递给函数 - someParameter。想象一下,someParameter并不像上面那样只是一个愚蠢的常量,而是一个数组中的元素,我们在render方法中的数组中得到map()。我们如何处理这种情况?也就是说,我们如何传递一个不是每次都从头创建的函数,从而打破了我们只在必要时才能巧妙地重新渲染的能力?

谢谢!

2 个答案:

答案 0 :(得分:2)

bind不仅可以绑定上下文,还可以绑定参数。所以你可以在构造函数中做这样的事情:

this.doSomething = this.doSomething.bind(this, someArgument)

只有在组件生命周期内someArgument没有发生变化时,上述解决方案才有效 如果这个参数是动态的,唯一的解决方案是使用像你的例子中的箭头函数 - 但是请注意,只有当这个函数作为prop组件传递给子组件(而不是HTML元素)时,才会产生额外的重新渲染。反应文件:

  

在大多数情况下,这很好。但是,如果此回调作为a传递   支持降低组件,这些组件可能会做额外的   重新渲染。

如果你没有将它传递给React子组件,它应该不是问题,不会导致任何额外的重新渲染。请注意,React组件和HTML标记之间存在差异。如果你使用箭头函数作为HTML元素的事件回调(例如,在map中生成的div),它将不会触发此div的额外渲染,因为它不是一个组件而且没有渲染方法。 React Virtual DOM算法将决定是否应更新与此div对应的实际DOM元素,在我们的情况下,现有DOM元素将不会更新(您可以使用开发工具进行检查)。

class Hello extends React.Component{
  constructor() {
    super();
    this.state = {test: 0};
  }
  componentDidMount() {
    var i =0;
    this.timer = setInterval(() => {
      i++;
    	this.setState({test: i})
    }, 1000)
  }
  componentWillUnmount() {
    clearInterval(this.timer)
  }
  render() {
    return (<div>{this.state.test}
      {
        [1, 4, 6].map((v) => {
          return (<div key={v} onClick={() => { console.log(this.state.test)}}>test</div>)
        })
      }
    </div>);
  }
};

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
    <!-- This element's contents will be replaced with your component. -->
</div>

您可以检查上面的代码段生成的outoput,看看div元素在DOM中没有更新,尽管它们的箭头函数定义为事件回调,而父组件则重新生成。

如果你需要将这个函数传递给子组件,你可以选择使用带有参数值的函数和prop来传递prop,以避免极端重新渲染(当然你还需要实现shouldComponentUpdate):

<SomeComponent myClick={this.doSomething} myArgument={someArgument} />

和SomeComponent中的某个地方:

//JSX in SomeComponent render method
<div onClick={() => this.props.myClick(this.props.myArgument)} >...</div>

您还可以在SomeComponent中创建其他方法,而不是使用箭头函数:

handleMyClick() {
   this.props.myClick(this.props.myArgument);
}

然后使用它JSX:

<div onClick={this.handleMyClick} >...</div>

总而言之:如果你使用箭头函数作为HTML标签上的事件回调,它不应该导致性能问题,也不会触发任何额外的重新渲染 - 它不会破坏仅在必要时巧妙地重新渲染的能力。只有当箭头功能作为支撑传递给子组件时,它才可能导致额外的重新渲染,在这种情况下,您可以使用我建议的洗脱剂。

答案 1 :(得分:0)

请您检查一下这里提出的解决方案&#34; Protips&#34;部分? https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md

看起来好像这可行:

    var List = createReactClass({
              render() {
        return (
          <ul>
            {this.props.items.map(item =>
              <ListItem key={item.id} item={item} onItemClick={this.props.onItemClick} />
            )}
          </ul>
        );
      }
    });

    var ListItem = createReactClass({
      render() {
        return (
          <li onClick={this._onClick}>
            ...
          </li>
        );
      },
      _onClick() {
        this.props.onItemClick(this.props.item.id);
      }
    });