перерисовать только одного ребенка с реагирующими крючками - PullRequest
1 голос
/ 03 июня 2019

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 />, и я ожидал повторного рендеринга <List /> и <Button A/>, но <ButtonB /> также был перерисован.

Я хочу заблокировать повторный рендеринг <ButtonB />, когда нажимаю <ButtonA />

Как мне этого добиться?

Ответы [ 3 ]

2 голосов
/ 03 июня 2019

Вы можете использовать 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>
0 голосов
/ 03 июня 2019

По умолчанию все дочерние элементы родительского элемента отображаются повторно, если состояние родительского элемента изменяется. Не имеет значения, имеет ли это изменение прямое влияние на ребенка или нет. Однако вы можете явно отключить повторную визуализацию конкретного компонента, используя метод жизненного цикла shouldComponentUpdate .

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>
0 голосов
/ 03 июня 2019

Оптимизация функционального компонента таким образом, чтобы React мог рассматривать его как чистый компонент, не обязательно требует преобразования компонента в компонент класса.

Если вы уже знакомы с пакетом перекомпоновки, вы знаете, что он предоставляет широкий набор компонентов более высокого порядка, что делает его очень полезным при работе с функциональными компонентами.

Пакет перекомпоновки экспортирует {чистый} компонент более высокого порядка, который пытается оптимизировать компонент React, предотвращая обновления компонента, если не был изменен реквизит, используя shallowEqual () для проверки на наличие изменений.

Используя чистый компонент высшего порядка, наш функциональный компонент можно обернуть следующим образом:

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);
...