我正在用React应用程序运行lint,收到以下错误:

error    JSX props should not use arrow functions        react/jsx-no-bind

这就是我运行箭头函数的地方(onClick以内):

{this.state.photos.map(tile => (
  <span key={tile.img}>
    <Checkbox
      defaultChecked={tile.checked}
      onCheck={() => this.selectPicture(tile)}
      style={{position: 'absolute', zIndex: 99, padding: 5, backgroundColor: 'rgba(255, 255, 255, 0.72)'}}
    />
    <GridTile
      title={tile.title}
      subtitle={<span>by <b>{tile.author}</b></span>}
      actionIcon={<IconButton onClick={() => this.handleDelete(tile)}><Delete color="white"/></IconButton>}
    >
      <img onClick={() => this.handleOpen(tile.img)} src={tile.img} style={{cursor: 'pointer'}}/>
    </GridTile>
  </span>
))}

这是一种应该避免的坏习惯吗?最好的方法是什么?

推荐答案

Why you shouldn't use inline arrow functions in JSX props

在JSX中使用箭头函数或绑定是一种不好的做法,会影响性能,因为每次渲染时都会重新创建该函数.

  1. 无论何时创建函数,前一个函数都会被垃圾收集.重新播放许多元素可能会在动画中产生jank.

  2. 使用内联箭头函数将导致PureComponent,而在shouldComponentUpdate方法中使用shallowCompare的组件无论如何都会重新加载.由于每次都会重新创建箭头功能props ,因此浅比较会将其标识为props 的更改,组件将重新加载.

正如您在下面的两个示例中所看到的,当我们使用内联箭头函数时,<Button>组件每次都会重新渲染(控制台显示"渲染按钮"文本).

Example 1 - PureComponent 100 inline handler

class Button extends React.PureComponent {
  render() {
    const { onClick } = this.props;
    
    console.log('render button');
    
    return (
      <button onClick={ onClick }>Click</button>
    );
  }
}

class Parent extends React.Component {
  state = {
    counter: 0
  }
  
  onClick = () => this.setState((prevState) => ({
    counter: prevState.counter + 1
  }));
  
  render() {
    const { counter } = this.state;
    
    return (
      <div>
        <Button onClick={ this.onClick } />
        <div>{ counter }</div>
      </div>
    );
  }
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Example 2 - PureComponent 100 inline handler

class Button extends React.PureComponent {
  render() {
    const { onClick } = this.props;
    
    console.log('render button');
    
    return (
      <button onClick={ onClick }>Click</button>
    );
  }
}

class Parent extends React.Component {
  state = {
    counter: 0
  }
  
  render() {
    const { counter } = this.state;
    
    return (
      <div>
        <Button onClick={ () => this.setState((prevState) => ({
          counter: prevState.counter + 1
        })) } />
        <div>{ counter }</div>
      </div>
    );
  }
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Binding methods to 100 without inlining arrow functions

  1. 在构造函数中手动绑定方法:

    class Button extends React.Component {
      constructor(props, context) {
        super(props, context);
    
        this.cb = this.cb.bind(this);
      }
    
      cb() {
    
      }
    
      render() {
        return (
          <button onClick={ this.cb }>Click</button>
        );
      }
    }
    
  2. 使用带箭头函数的proposal-class-fields绑定方法.由于这是一个第三阶段的建议,您需要将Stage 3 presetClass properties transform添加到您的babel配置中.

    class Button extends React.Component {
      cb = () => { // the class property is initialized with an arrow function that binds this to the class
    
      }
    
      render() {
        return (
          <button onClick={ this.cb }>Click</button>
        );
      }
    }
    

具有内部回调的函数组件

当我们在函数组件内创建内部函数(例如事件处理程序)时,每次呈现该组件时都会重新创建该函数.如果函数作为props (或通过上下文)传递给子组件(本例中为Button),则该子组件也将重新渲染.

Example 1 - Function Component with an inner callback:

const { memo, useState } = React;

const Button = memo(({ onClick }) => console.log('render button') || (
  <button onClick={onClick}>Click</button>
));

const Parent = () => {
  const [counter, setCounter] = useState(0);
  
  const increment = () => setCounter(counter => counter + 1); // the function is recreated all the time
  
  return (
    <div>
      <Button onClick={increment} />
      
      <div>{counter}</div>
    </div>
  );
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

为了解决这个问题,我们可以用useCallback() hook包装回调,并将依赖项设置为空array.

Note:生成的useState函数接受提供当前状态的更新程序函数.通过这种方式,我们不需要将当前状态设置为useCallback.

Example 2 - Function Component with an inner callback wrapped with useCallback:

const { memo, useState, useCallback } = React;

const Button = memo(({ onClick }) => console.log('render button') || (
  <button onClick={onClick}>Click</button>
));

const Parent = () => {
  const [counter, setCounter] = useState(0);
  
  const increment = useCallback(() => setCounter(counter => counter + 1), []);
  
  return (
    <div>
      <Button onClick={increment} />
      
      <div>{counter}</div>
    </div>
  );
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

Reactjs相关问答推荐

如何将google地址lat收件箱设置为输入值?

构建next.js项目时状态不变,但在dev服务器中

单击按钮时在子元素中不显示任何内容-react

将对象添加到集合中的数组时的no_id字段

使用REACT-RUTER-DOM链接仅更新一个搜索参数

使用Ionic 7和React 18,如何访问历史对象以在页面之间导航?

为什么React UseEffect挂钩在页面重新加载时运行

Github 操作失败:发布 NPM 包

Javascript - 正则表达式不适用于 MAC OS - 编码?

useRef 不关注模态

React 测试库包装器组件

如何到达类组件中的状态?

Redux Toolkit 配置,是否适用于全栈应用程序?

如何在 ReactJS 中提交后删除 URL 中的查询参数

使用 react-markdown 组件时 Tailwind CSS 的问题

NextJS htaccess 设置

如何通过onpress on view in react native flatlist来降低和增加特定视图的高度?

如何向 surveyjs 调查添加多个结尾?

如何在react 中更新子组件中的变量?

加密 Django Rest Framework API 响应 + 解密响应应用程序中的响应