react 注意事项

<CommentItem likeComment={() => this.likeComment(user.id)} />

这个问题会导致每次父组件render方法被调用时,一个新的函数被创建,已将其传入likeComment。这会有一个改变每个子组件props的副作用,它将会造成他们全部重新渲染,即使数据本身没有发生变化

子组件的likeComment属性将总是有相同的引用,这样就不会造成不必要的重新渲染

// 方法1 
import { useCallback } from 'react';

const List = ()=> {
  const comment = (id) => { doing something}
  const likeComment = useCallback(()=> { comment(id) }, [id]);
  // 这样自组建就不会从新渲染
  return list.map (
    v =>  <CommentItem onClick={likeComment(v.id)} >) 
}

然后再子组件中创建一个引用了传入属性的类方法:

//方法2
<CommentItem likeComment={this.likeComment} userID={user.id} />

class CommentItem extends PureComponent {
  ...
  handleLike() {
    this.props.likeComment(this.props.userID)
  }
  ...
}

不要在render方法里派生数据

每次组件重新渲染时topTen都将有一个新的引用,即使posts没有改变并且派生数据也是相同的。这将造成列表不必要的重新渲染。

render() {
  const { posts } = this.props
  const topTen = posts.sort((a, b) => b.likes - a.likes).slice(0, 9)
  return //...
}

使用派生方法解决

static getDerivedStateFromProps(nextProps, state) {
   if (
      props.list !== state.prevPropsList ||
      state.prevFilterText !== state.filterText
    )
}

管理派生 state 本来就很复杂,而且这种复杂度是随着需要管理的属性变得越来越庞大。比如,如果我们想在组件 state 里添加第二个派生 state,那就需要写两份跟踪变化的逻辑。

import memoize from "memoize-one";

class Example extends Component {
  // state 只需要保存当前的 filter 值:
  state = { filterText: "" };

  // 在 list 或者 filter 变化时,重新运行 filter:
  filter = memoize(
    (list, filterText) => list.filter(item => item.text.includes(filterText))
  );

  handleChange = event => {
    this.setState({ filterText: event.target.value });
  };

  render() {
    // 计算最新的过滤后的 list。
    // 如果和上次 render 参数一样,`memoize-one` 会重复使用上一次的值。
    const filteredList = this.filter(this.props.list, this.state.filterText);

    return (
      <Fragment>
        <input onChange={this.handleChange} value={this.state.filterText} />
        <ul>{filteredList.map(item => <li key={item.id}>{item.text}</li>)}</ul>
      </Fragment>
    );
  }
}

在使用 memoization 时,请记住这些约束:

  1. 大部分情况下, 每个组件内部都要引入 memoized 方法,已免实例之间相互影响。
  2. 一般情况下,我们会限制 memoization 帮助函数的缓存空间,以免内存泄漏。(上面的例子中,使用 memoize-one 只缓存最后一次的参数和结果)。
  3. 如果每次父组件都传入新的 props.list ,那本文提到的问题都不会遇到。在大多数情况下,这种方式是可取的。

如果你正在使用Redux,可以考虑使用reselect来创建”selectors”来组合和缓存派生数据。

One Reply to “react 注意事项”

Leave a Reply

Your email address will not be published. Required fields are marked *