re-render 只有一个 child 和 react-hooks

re-render only one child with react-hooks

const {useRef, useState} = React;

function List(){
  const renderCount = useRef(0);
  console.log('<List /> is rendered', ++renderCount.current);
  const [isClicked, setIsClicked] = useState(false);
  const toggle = () => setIsClicked(!isClicked)
  return (
    <div>
      <ButtonA onClick={toggle} isClicked={isClicked} />
      <ButtonB />
    </div>
  )
}

function ButtonA(props){
  const renderCount = useRef(0);
  console.log('<ButtonA /> is rendered', ++renderCount.current);

  return (<button onClick={props.onClick} className={`${props.isClicked ? 'true':'false'}`} >Button A</button>);
}

function ButtonB(){
  const renderCount = useRef(0);
  console.log('<ButtonB /> is rendered', ++renderCount.current);
  
  return (<button>Button B </button>);
}


ReactDOM.render(
  <List />, document.getElementById('root')
)
button.true{
  background-color: red;
}

button.false{
  background-color: blue;
  
  }
<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>

这是示例代码。

当我点击 <ButtonA /> 时,我期望 re-rendering <List /><Button A/>,但 <ButtonB /> 也是 re-rendered。

我想在点击 <ButtonA />

时屏蔽 re-rendering <ButtonB />

如何实现?

优化一个功能组件以便 React 可以将其视为纯组件,不一定需要将组件转换为 class 组件。

如果您已经熟悉 recompose 包,那么您就会知道它提供了广泛的 higher-order 组件集合,这使得它在处理功能组件时非常有用。

recompose 包导出一个{纯} higher-order 组件,该组件尝试通过防止组件更新来优化 React 组件,除非 prop 已更改,使用 shallowEqual() 来测试更改。

使用纯 higher-order 组件,我们的功能组件可以包装如下:

import React from 'react';
import { pure } from 'recompose';

function ButtonB() {
  const renderCount = useRef(0);
  console.log('<ButtonB /> is rendered', ++renderCount.current);

  return (<button>Button B </button>);
}
// Wrap component using the `pure` HOC from recompose
export default pure(ButtonB);

默认情况下,如果 parent 的状态发生变化,parent 的所有 children 都是 re-rendered。此更改是否对 child 有直接影响并不重要。 但是,您可以使用 shouldComponentUpdate 生命周期方法显式禁用特定组件的 re-render。

class Button extends React.Component {
  shouldComponentUpdate(){
    return !this.props.shouldNotUpdate;
  }
  
  render(){
    const { id, onClick } = this.props;
    console.log(`button ${this.props.id} rendered`)
    return <button onClick={onClick}>{`Button ${id}`}</button>
  }
}

class App extends React.Component {
  state = { clicked: 0 }
  
  handleClick = () => this.setState(({clicked}) => ({clicked: clicked + 1}))
  
  render(){
    return (
      <div>
        {this.state.clicked}<br />
        <Button id="1" onClick={this.handleClick} />
        <Button id="2" shouldNotUpdate />
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

您可以利用 React.memo 获得与功能组件 shouldComponentUpdate 相同的功能

const {useRef, useState} = React;

function List(){
  const renderCount = useRef(0);
  console.log('<List /> is rendered', ++renderCount.current);
  const [isClicked, setIsClicked] = useState(false);
  const toggle = () => setIsClicked(!isClicked)
  return (
    <div>
      <ButtonA onClick={toggle} isClicked={isClicked} />
      <ButtonB />
    </div>
  )
}

function ButtonA(props){
  const renderCount = useRef(0);
  console.log('<ButtonA /> is rendered', ++renderCount.current);

  return (<button onClick={props.onClick} className={`${props.isClicked ? 'true':'false'}`} >Button A</button>);
}

const ButtonB = React.memo(() => {
  const renderCount = useRef(0);
  console.log('<ButtonB /> is rendered', ++renderCount.current);
  
  return (<button>Button B </button>);
})


ReactDOM.render(
  <List />, document.getElementById('root')
)
button.true{
  background-color: red;
}

button.false{
  background-color: blue;
  
  }
<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>