React/jsx-no-lambda

React/jsx-no-lambda

lambda in JSX

在 JSX 中,很常见一种的用法是通过定义“匿名” lambda (即 arrow function)传递当前闭包环境里的参数。这种用于常用于以下情形:

  1. 事件监听函数 (如onClick)
  2. 多重 Array.prototype.map(itemRender)

JSX 里写 lambda 的缺点主要是影响性能:

  1. 每次渲染(render)时都会重新创建一次lambda函数对象。在循环次数较多时,可能造成性能问题。
  2. 如果将 lambda 作为props传给PureComponent类型的子组件,那么子组件在每次父组件render时都会render,完全失去了PureComponent的性能优势。(因为每次render时父组件都会创建全新的 lambda)

举例说明如下:

事件监听函数

function TodosComponent({ todos, onClick }) {
  return todos.map((todo, i) => (
    <div key={todo.id} onClick={e => onClick(todo)}>
      {todo.title}
    </div>
  ));
}

这里通过 lambda 传递当前触发了 onClick 的 todo 对象参数。但代价时每个 todo 渲染时都会创建一个单独的lambda函数。

也可以使用bind,例如 onClick={onClick.bind(this, todo)}。但 bind 本质上也是创建了一个新函数,无法提高性能。

多重 Array.prototype.map(itemRender)

function TodosComponent({ todos, onClick }) {
  return todos.map((todo, i) => (
    <div key={todo.id}>
      {todo.title}
      {todo.tags.map(tag => <span key={tag}>{tag}</span>)}
    </div>
  ));
}

这种情况下,每个 todo 渲染时都重新创建了一个 tag => <span key={tag}>{tag}</span> 的 lambda。

性能优化

可以通过优化去掉 lambda

事件监听函数

在 render 时通过自定义的 data-XX 属性将信息存储到DOM节点,然后在事件处理函数里通过 event.currentTarget.attributes.getNamedItem('data-XX') 或 event.currentTarget.dataset.XX 获取之前保存的值。这样即使不使用 lambda 也能给事件监听函数传递参数。

function TodosComponent({ todos, onClick }) {
  return todos.map((todo, i) => (
    <div key={todo.id} data-index={i} onClick={_onClick}>
      {todo.title}
    </div>
  ));

  function _onClick(event) {
    let index = event.currentTarget.attributes.getNamedItem('data-index').value;
    let todo = todos[index];
    onClick(todo);
  }
}

多重 Array.prototype.map(itemRender)

如果内层的 lambda 不需要访问外层的闭包变量(比如上面的示例),那么直接将 lambda 提取出来定义函数即可:

function TodosComponent({ todos, onClick }) {
  return todos.map((todo, i) => (
    <div key={todo.id}>
      {todo.title}
      {todo.tags.map(tagRender)}
    </div>
  ));

  function tagRender(tag) {
    return <span key={tag}>{tag}</span>;
  }
}

但如果内层的 lambda 里需要访问闭包变量(比如 tagRender 里需要获取当前的 todo 对象),那么就不能直接用上面写法。而需要消除掉 Array.prototype.map调用,改用循环和中间函数写法。


Last update: 2018-09-05 09:19:12 UTC