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>
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 />
<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>