React 单组件状态管理

对于 Class 组件,不管我们的 state 变没变,只要执行this.setState都会调用render

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      count: 1
    };
  }
  onClick = () => {
    this.setState(this.state);
  };
  render() {
    console.log("render 执行了");
    return (
      <div>
        {this.state.count}
        <button onClick={this.onClick}>+1</button>
      </div>
    );
  }
}

上面代码每次点击+1就会执行render

函数组件如果对应 state 的引用没变的话,就不会调用render

export default function App () {
  const [state, setState] = useState({ count: 1 });
  const onClick = () => {
    setState(state) // 不会变
    setState({count: state.count + 1})
  }
  return (
    <div>
      {state.count}
      <button onClick={onClick}>+1</button>
    </div>
  )
}

使用 shouldComponentUpdate 阻止多余的 render

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      count: 1,
      other: "其他属性"
    };
  }
  onClick = () => {
    this.setState({
      ...this.state,
      count: 1
    });
  };
  shouldComponentUpdate(newProps, newState) {
    for (let key in newState) {
      if (newState[key] !== this.state[key]) {
        return true; // state 变了就 render
      }
    }
    for (let key in newProps) {
      if (newProps[key] !== this.props[key]) {
        return true; // props 变了就 render
      }
    }
    console.log("此处阻止 render");
    return false; // 没变就阻止 render
  }
  render() {
    console.log("render 执行了");
    return (
      <div>
        {this.state.count}
        <button onClick={this.onClick}>+1</button>
      </div>
    );
  }
}

我们也可以使用pureComponent 代替 shouldComponentUpdate

class App extends React.PureComponent {
  constructor() {
    super();
    this.state = {
      count: 1,
      other: "其他属性"
    };
  }
  onClick = () => {
    this.setState({
      ...this.state,
      count: 1
    });
  };
  render() {
    console.log("render 执行了");
    return (
      <div>
        {this.state.count}
        <button onClick={this.onClick}>+1</button>
      </div>
    );
  }
}

使用 immer.js 修改深层级 state

上面如果我们的state层级很深,我们修改起来就会很困难,这时候我们可以使用 immer.js,它会让我们在创建新对象写起来就像是在修改原对象

const newData = produce(data, (draft) => {
  draft.user.group.name = "group 2";
});

代码中的 draft 实际上是个 proxy,会把你的修改收集起来,然后创建新对象,而且 immer 创建新对象的方法很智能,并不是直接深拷贝

immer.js 如何得到新的数据

上面我们修改 state.user.group.name 它只会把我们需要修改的这四个节点变成新的节点,其他的都直接复制过来(浅拷贝),所以我们图中左右两个 other 是同一个对象,只有在你修改 other 时才会创建新的对象,immer.js 永远不会去修改原数据

该用 Class 组件还是函数组件?

总结

  • state 内容变了的时候
    引用可以不变 state.user.group.name = 'xxx'(不推荐)
    引用可以变 state = {...state, user: xxx}(推荐用 immer)

  • 何时 render*
    Class 组件看到一个或多个 setState 就 render
    函数组件的发现 state 引用变了才会 render

  • render 做了什么
    render 会创建虚拟 DOM,经过 diff,最后选择性地更新真实 DOM
    所以多余的 render 很可能不会浪费多少性能,没必要阻止
    如果阻止 render 的代码如果太复杂,可能比 render 还要耗性能

  • immer.js 的用法
    像修改原数据一样,但实际得到新数据
    新数据保有部分旧数据的引用,不会浪费内存
    与函数组件非常契合

  • 为什么不能直接修改 state
    因为出 bug 的概率更大

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容