【问题标题】:No .bind() or Arrow Functions in JSX Props making no sense to me when I need to pass arguments当我需要传递参数时,JSX Props 中没有 .bind() 或箭头函数对我来说毫无意义
【发布时间】:2017-02-14 03:19:31
【问题描述】:

看看 eslint 规则,No .bind() or Arrow Functions in JSX Props

它说不使用箭头函数或绑定:

<div onClick={this._handleClick.bind(this)}></div>
<div onClick={() => console.log('Hello!'))}></div>

而是使用:

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

这一切都很好,但是如何在点击事件上将参数传递给这个函数?

这是我的幼稚代码示例:

export class UserList extends Component {
  constructor(props) {
    super(props);
    this.handleToggleActive = this.handleToggleActive.bind(this);
  }
  handleToggleActive() {
    console.log(arguments);
  }
  render() {
    return (
      <ul className="user-list">
        {this
          .props
          .users
          .map(user => (
            <li key={user.id}>
              <Link to={`/users/${user.id}`}>{user.name}</Link>
              <Button onClick={this.handleToggleActive}>Toggle Active</Button>
            </li>
          ))}
      </ul>
    );
  }
}

如何将user.id 加入我的handleToggleActive 函数?当我尝试类似:

<Button onClick={this.handleToggleActive(user.id)}>Toggle Active</Button>

它在渲染时执行,我能看到的唯一其他方法是使用箭头函数?

这样做的正确方法是什么?

【问题讨论】:

标签: javascript reactjs eslint


【解决方案1】:

使用绑定和箭头函数都会创建一个新函数,将一个全新的 prop 传递给您的组件,从而导致不必要的重新渲染和垃圾收集器的额外工作,使用 data-attributes 是一个糟糕的设计,并且不是 React 的方式做事,你不应该直接访问 DOM,而是让 React 处理事情。取而代之的是,您可以只创建一个新组件并将处理程序作为道具传递,在组件的方法中处理 id 的“绑定”:

class User extends Component {
  handleClick() {
    this.props.handleToggleActive(this.props.user.id);
  }

  render() {
    return (
       <li>
         <Link to={`/users/${this.props.user.id}`}>{this.props.user.name}</Link>
         <Button onClick={this.handleClick}>Toggle Active</Button>
      </li>
    )
  } 
}

export class UserList extends Component {
  constructor(props) {
    super(props);
    this.handleToggleActive = this.handleToggleActive.bind(this);
  }
  handleToggleActive(userId) {
    console.log(userId);
  }
  render() {
    return (
      <ul className="user-list">
        {this
          .props
          .users
          .map(user => (
            <User key={user.id} user={user} handleToggleActive={this.handleToggleActive} />
          ))}
      </ul>
    );
  }
}

【讨论】:

  • 存在过度优化之类的东西。使处理程序成为返回新函数的函数不可能对效率造成足够的损害,以至于证明创建一个全新的组件是合理的。对于可能会或可能不会影响应用程序生命周期的流程,这似乎是过度设计。指出这种方法的缺点绝对是好的,但我认为这两种解决方案都是有效的。我完全同意你的数据属性之一。
  • @ZekeDroid 我不知道该怎么说,但是您能否为您的陈述提供实际的论据,也许您应该在否决唯一正确答案之前弄清楚您的事实?我建议你使用 componentWillUpdate 生命周期方法做一个小的概念证明,也许你会意识到避免 eslint 规则和尊重它是两件不同的事情。创建这个 eslint 规则是有充分理由的,你认为创建全新的函数并在每个渲染步骤中传递这些函数是个好主意吗,请继续,这对于一个简单的应用程序来说应该没问题
  • 此规则实际超出的地方是在接收大量更新的大型应用程序中,在这种情况下,渲染绑定或在渲染中传递全新的函数将导致实际性能问题。
  • 实际上从结构的角度来看,在这种特定情况下,使用单独的用户组件而不是 map 函数中的普通标记更有意义,这不是“过度工程”或“过度优化”。
  • 不是故意让你不高兴的!只是作为建设性的批评,哇,根本不是要点击你的反对票,我实际上是想对数据投反对票,因为那确实违反了 React 原则。但我发现投反对票对 SO 有非常负面的影响,所以我将避免在这里使用它,并尝试删除你的投反对票。我不确定您的意思是“实际参数”吗?我认为很清楚我指的是什么。一个全新的组件与牺牲在渲染上创建一个新函数之间的权衡
【解决方案2】:

我猜你可以使用data-* 属性。

export class UserList extends Component {
    constructor(props) {
        super(props);
        this.handleToggleActive = this.handleToggleActive.bind(this);
    }
    handleToggleActive(event) {
        console.log(event.target.dataset.id);
    }
    render() {
        return (
            <ul className="user-list">
                {this
                .props
                .users
                .map(user => (
                    <li key={user.id}>
                    <Link to={`/users/${user.id}`}>{user.name}</Link>
                    <Button onClick={this.handleToggleActive} data-id={user.id}>Toggle Active</Button>
                    </li>
                ))}
            </ul>
        );
    }
}

【讨论】:

  • 你应该使用currentTarget
  • 使用 data- 属性是一个糟糕的设计,让我想起了 jquery 时代,它并不代表 React 的做事方式,而不是你自己访问 DOM 你应该让 React 为你处理事情。
【解决方案3】:

为了解决性能问题(这就是规则存在的原因),使用 memoization 怎么样?

例如,使用包lru-memoize 你会得到(带有一个哑组件)类似的东西:

var memoize100 = require('lru-memoize')(100);

let handleToggleActive = (user) => () => {
    console.log(user.id)
}
handleToggleActive = memoize100(handleToggleActive)

const UserList = (props) =>
  <ul className="user-list">
  { props.users
      .map(user => (
        <li key={user.id}>
          <Link to={`/users/${user.id}`}>{user.name}</Link>
          <Button onClick={handleToggleActive(user)}>Toggle Active</Button>
        </li>
      ))
    }
  </ul>

【讨论】:

    【解决方案4】:

    使用箭头函数或绑定将在每次渲染组件时创建不同的回调实例。因此,每次都重新创建该函数,并且旧的函数被垃圾收集,垃圾收集器过载。另外,由于这个原因,shouldcomponentupdate 中的纯组件和浅比较的组件将始终重新渲染,因为它会发现其 props 的变化,因为每次渲染父组件时都会丢弃旧函数并创建一个新函数。

    我们可以使用属性初始化语法和柯里化来避免在 JSX 代码中使用绑定或箭头函数。

    handleClick = (param) => (e) => {
      console.log('Event', e);
      console.log('Parameter', param);
    }
    
    render() {
      <button onClick={this.handleClick('Parameter')}></button>
    }
    

    【讨论】:

      猜你喜欢
      • 2015-06-19
      • 2022-01-10
      • 2020-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多