admin管理员组文章数量:1023571
My code has a ponent that takes both props and has its own internal state.
The ponent should rerender ONLY when its props change. State changes should NOT trigger a rerender.
This behaviour can be implemented with a class based ponent and a custom shouldComponentUpdate
function.
However, this would be the first class based ponent in the codebase. Everything is done with functional ponents and hooks.
Therefore I would like to know whether it is possible to code the desired functionality with functional ponents.
After a few answers that didn't approach the real problem, I think I have to reformulate my question. Here is a minimal example with two ponents:
- Inner takes a prop and has state. This is the ponent in question. It must not rerender after state changes. Prop changes should trigger a rerender.
- Outer is a wrapper around inner. It has no meaning in the scope of this question and is only there to give props to Inner and to simulate prop changes.
To demonstrate the desired functionality I have implemented Inner with a class based ponent. A live version of this code can be found on codesandbox. How can I migrate it to a functional ponent:
Inner.tsx
:
import React, { Component } from 'react'
interface InnerProps{outerNum:number}
interface InnerState{innerNum:number}
export default class Inner extends Component<InnerProps, InnerState> {
state = {innerNum:0};
shouldComponentUpdate(nextProps:InnerProps, nextState:InnerState){
return this.props != nextProps;
}
render() {
return (
<button onClick={()=>{
this.setState({innerNum: Math.floor(Math.random()*10)})
}}>
{`${this.props.outerNum}, ${this.state.innerNum}`}
</button>
)
}
}
Outer.tsx:
import React, { useState } from "react";
import Inner from "./Inner";
export default function Outer() {
const [outerState, setOuterState] = useState(1);
return (
<>
<button
onClick={() => {
setOuterState(Math.floor(Math.random() * 10));
}}
>
change outer state
</button>
<Inner outerNum={outerState}></Inner>
</>
);
}
The official docs say to wrap the ponent in React.memo
. But this doesn't seem to work for preventing rerenders on state change. It only applies to prop changes.
I have tried to make React.memo
work. You can see a version of the code with both Outer and Inner being functional ponents here.
Related questions:
How to use shouldComponentUpdate with React Hooks? : This question only deals with prop changes. The accepted answer advises to use React.memo
shouldComponentUpdate in function ponents : This question predates stateful functional ponents. The accepted answer explains how functional ponents don't need shouldComponentUpdate
since they are stateless.
My code has a ponent that takes both props and has its own internal state.
The ponent should rerender ONLY when its props change. State changes should NOT trigger a rerender.
This behaviour can be implemented with a class based ponent and a custom shouldComponentUpdate
function.
However, this would be the first class based ponent in the codebase. Everything is done with functional ponents and hooks.
Therefore I would like to know whether it is possible to code the desired functionality with functional ponents.
After a few answers that didn't approach the real problem, I think I have to reformulate my question. Here is a minimal example with two ponents:
- Inner takes a prop and has state. This is the ponent in question. It must not rerender after state changes. Prop changes should trigger a rerender.
- Outer is a wrapper around inner. It has no meaning in the scope of this question and is only there to give props to Inner and to simulate prop changes.
To demonstrate the desired functionality I have implemented Inner with a class based ponent. A live version of this code can be found on codesandbox. How can I migrate it to a functional ponent:
Inner.tsx
:
import React, { Component } from 'react'
interface InnerProps{outerNum:number}
interface InnerState{innerNum:number}
export default class Inner extends Component<InnerProps, InnerState> {
state = {innerNum:0};
shouldComponentUpdate(nextProps:InnerProps, nextState:InnerState){
return this.props != nextProps;
}
render() {
return (
<button onClick={()=>{
this.setState({innerNum: Math.floor(Math.random()*10)})
}}>
{`${this.props.outerNum}, ${this.state.innerNum}`}
</button>
)
}
}
Outer.tsx:
import React, { useState } from "react";
import Inner from "./Inner";
export default function Outer() {
const [outerState, setOuterState] = useState(1);
return (
<>
<button
onClick={() => {
setOuterState(Math.floor(Math.random() * 10));
}}
>
change outer state
</button>
<Inner outerNum={outerState}></Inner>
</>
);
}
The official docs say to wrap the ponent in React.memo
. But this doesn't seem to work for preventing rerenders on state change. It only applies to prop changes.
I have tried to make React.memo
work. You can see a version of the code with both Outer and Inner being functional ponents here.
Related questions:
How to use shouldComponentUpdate with React Hooks? : This question only deals with prop changes. The accepted answer advises to use React.memo
shouldComponentUpdate in function ponents : This question predates stateful functional ponents. The accepted answer explains how functional ponents don't need shouldComponentUpdate
since they are stateless.
-
React.memo: "This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs." - Could you present us the case where you might need it? Maybe we can suggest other solutions such as introducing the
key
property. But its hard to reason about the problem without the case itself. – Caramiriel Commented Mar 31, 2020 at 17:00
4 Answers
Reset to default 1React memo do not stop state changes
React.memo only checks for prop changes. If your function ponent wrapped in React.memo has a useState or useContext Hook in its implementation, it will still rerender when state or context change.
Ref:- https://reactjs/docs/react-api.html#reactmemo
React is by design driven by setState -> re-render loop. Props change is in fact a setState somewhere in parent ponents. If you don't want the setState to trigger a re-render, then why in the first place use it?
You can pull in a const state = useRef({}).current
to store your internal state instead.
function InnerFunc(props) {
const state = useRef({ innerNum: 0 }).current;
return (
<button
onClick={() => {
state.innerNum = Math.floor(Math.random() * 10);
}}
>
{`${props.outerNum}, ${state.innerNum}`}
</button>
);
}
That said, it's still a valid question to ask: "how to implement shouldComponentUpdate in a react hook fashion?" Here's the solution:
function shouldComponentUpdate(elements, predicate, deps) {
const store = useRef({ deps: [], elements }).current
const shouldUpdate = predicate(store.deps)
if (shouldUpdate) {
store.elements = elements
}
store.deps = deps
return store.elements
}
// Usage:
function InnerFunc(props) {
const [state, setState] = useState({ innerNum: 0 })
const elements = (
<button
onClick={() => {
setState({ innerNum: Math.floor(Math.random() * 10) });
}}
>
{`${props.outerNum}, ${state.innerNum}`}
</button>
);
return shouldComponentUpdate(elements, (prevDeps) => {
return prevDeps[0] !== props
}, [props, state])
}
Noted that it's impossible to prevent a re-render cycle when setState
is called, the above hook merely makes sure the re-rendered result stays the same as prev rendered result.
Your Inner
ponent depends on the property num
of the Outer
ponent, you can't prevent it from rendering on property change as React.memo
makes properties parison:
// The default behaviour is shallow parison between previous and current render properties.
const areEqual = (a, b) => a.num === b.num;
export default React.memo(Inner, areEqual);
By memoizing the Inner
ponent and removing the num
dependency, it won't render on Outer
rendering, see sandbox attached.
export default function Outer() {
const [outerState, setOuterState] = useState(1);
return (
<>
...
// v Inner is memoized and won't render on `outerState` change.
<Inner />
</>
);
}
If you want to implement shouldComponentUpdate
with hooks you can try:
const [currState] = useState();
// shouldUpdateState your's custom function to pare and decide if update state needed
setState(prevState => {
if(shouldUpdateState(prevState,currState)) {
return currState;
}
return prevState;
});
you should use the event that provide the browser and capture in the function before setState, like this
function setState = (e) =>{ //the e is the event that give you the browser
//changing the state
e.preventDefault();
}
My code has a ponent that takes both props and has its own internal state.
The ponent should rerender ONLY when its props change. State changes should NOT trigger a rerender.
This behaviour can be implemented with a class based ponent and a custom shouldComponentUpdate
function.
However, this would be the first class based ponent in the codebase. Everything is done with functional ponents and hooks.
Therefore I would like to know whether it is possible to code the desired functionality with functional ponents.
After a few answers that didn't approach the real problem, I think I have to reformulate my question. Here is a minimal example with two ponents:
- Inner takes a prop and has state. This is the ponent in question. It must not rerender after state changes. Prop changes should trigger a rerender.
- Outer is a wrapper around inner. It has no meaning in the scope of this question and is only there to give props to Inner and to simulate prop changes.
To demonstrate the desired functionality I have implemented Inner with a class based ponent. A live version of this code can be found on codesandbox. How can I migrate it to a functional ponent:
Inner.tsx
:
import React, { Component } from 'react'
interface InnerProps{outerNum:number}
interface InnerState{innerNum:number}
export default class Inner extends Component<InnerProps, InnerState> {
state = {innerNum:0};
shouldComponentUpdate(nextProps:InnerProps, nextState:InnerState){
return this.props != nextProps;
}
render() {
return (
<button onClick={()=>{
this.setState({innerNum: Math.floor(Math.random()*10)})
}}>
{`${this.props.outerNum}, ${this.state.innerNum}`}
</button>
)
}
}
Outer.tsx:
import React, { useState } from "react";
import Inner from "./Inner";
export default function Outer() {
const [outerState, setOuterState] = useState(1);
return (
<>
<button
onClick={() => {
setOuterState(Math.floor(Math.random() * 10));
}}
>
change outer state
</button>
<Inner outerNum={outerState}></Inner>
</>
);
}
The official docs say to wrap the ponent in React.memo
. But this doesn't seem to work for preventing rerenders on state change. It only applies to prop changes.
I have tried to make React.memo
work. You can see a version of the code with both Outer and Inner being functional ponents here.
Related questions:
How to use shouldComponentUpdate with React Hooks? : This question only deals with prop changes. The accepted answer advises to use React.memo
shouldComponentUpdate in function ponents : This question predates stateful functional ponents. The accepted answer explains how functional ponents don't need shouldComponentUpdate
since they are stateless.
My code has a ponent that takes both props and has its own internal state.
The ponent should rerender ONLY when its props change. State changes should NOT trigger a rerender.
This behaviour can be implemented with a class based ponent and a custom shouldComponentUpdate
function.
However, this would be the first class based ponent in the codebase. Everything is done with functional ponents and hooks.
Therefore I would like to know whether it is possible to code the desired functionality with functional ponents.
After a few answers that didn't approach the real problem, I think I have to reformulate my question. Here is a minimal example with two ponents:
- Inner takes a prop and has state. This is the ponent in question. It must not rerender after state changes. Prop changes should trigger a rerender.
- Outer is a wrapper around inner. It has no meaning in the scope of this question and is only there to give props to Inner and to simulate prop changes.
To demonstrate the desired functionality I have implemented Inner with a class based ponent. A live version of this code can be found on codesandbox. How can I migrate it to a functional ponent:
Inner.tsx
:
import React, { Component } from 'react'
interface InnerProps{outerNum:number}
interface InnerState{innerNum:number}
export default class Inner extends Component<InnerProps, InnerState> {
state = {innerNum:0};
shouldComponentUpdate(nextProps:InnerProps, nextState:InnerState){
return this.props != nextProps;
}
render() {
return (
<button onClick={()=>{
this.setState({innerNum: Math.floor(Math.random()*10)})
}}>
{`${this.props.outerNum}, ${this.state.innerNum}`}
</button>
)
}
}
Outer.tsx:
import React, { useState } from "react";
import Inner from "./Inner";
export default function Outer() {
const [outerState, setOuterState] = useState(1);
return (
<>
<button
onClick={() => {
setOuterState(Math.floor(Math.random() * 10));
}}
>
change outer state
</button>
<Inner outerNum={outerState}></Inner>
</>
);
}
The official docs say to wrap the ponent in React.memo
. But this doesn't seem to work for preventing rerenders on state change. It only applies to prop changes.
I have tried to make React.memo
work. You can see a version of the code with both Outer and Inner being functional ponents here.
Related questions:
How to use shouldComponentUpdate with React Hooks? : This question only deals with prop changes. The accepted answer advises to use React.memo
shouldComponentUpdate in function ponents : This question predates stateful functional ponents. The accepted answer explains how functional ponents don't need shouldComponentUpdate
since they are stateless.
-
React.memo: "This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs." - Could you present us the case where you might need it? Maybe we can suggest other solutions such as introducing the
key
property. But its hard to reason about the problem without the case itself. – Caramiriel Commented Mar 31, 2020 at 17:00
4 Answers
Reset to default 1React memo do not stop state changes
React.memo only checks for prop changes. If your function ponent wrapped in React.memo has a useState or useContext Hook in its implementation, it will still rerender when state or context change.
Ref:- https://reactjs/docs/react-api.html#reactmemo
React is by design driven by setState -> re-render loop. Props change is in fact a setState somewhere in parent ponents. If you don't want the setState to trigger a re-render, then why in the first place use it?
You can pull in a const state = useRef({}).current
to store your internal state instead.
function InnerFunc(props) {
const state = useRef({ innerNum: 0 }).current;
return (
<button
onClick={() => {
state.innerNum = Math.floor(Math.random() * 10);
}}
>
{`${props.outerNum}, ${state.innerNum}`}
</button>
);
}
That said, it's still a valid question to ask: "how to implement shouldComponentUpdate in a react hook fashion?" Here's the solution:
function shouldComponentUpdate(elements, predicate, deps) {
const store = useRef({ deps: [], elements }).current
const shouldUpdate = predicate(store.deps)
if (shouldUpdate) {
store.elements = elements
}
store.deps = deps
return store.elements
}
// Usage:
function InnerFunc(props) {
const [state, setState] = useState({ innerNum: 0 })
const elements = (
<button
onClick={() => {
setState({ innerNum: Math.floor(Math.random() * 10) });
}}
>
{`${props.outerNum}, ${state.innerNum}`}
</button>
);
return shouldComponentUpdate(elements, (prevDeps) => {
return prevDeps[0] !== props
}, [props, state])
}
Noted that it's impossible to prevent a re-render cycle when setState
is called, the above hook merely makes sure the re-rendered result stays the same as prev rendered result.
Your Inner
ponent depends on the property num
of the Outer
ponent, you can't prevent it from rendering on property change as React.memo
makes properties parison:
// The default behaviour is shallow parison between previous and current render properties.
const areEqual = (a, b) => a.num === b.num;
export default React.memo(Inner, areEqual);
By memoizing the Inner
ponent and removing the num
dependency, it won't render on Outer
rendering, see sandbox attached.
export default function Outer() {
const [outerState, setOuterState] = useState(1);
return (
<>
...
// v Inner is memoized and won't render on `outerState` change.
<Inner />
</>
);
}
If you want to implement shouldComponentUpdate
with hooks you can try:
const [currState] = useState();
// shouldUpdateState your's custom function to pare and decide if update state needed
setState(prevState => {
if(shouldUpdateState(prevState,currState)) {
return currState;
}
return prevState;
});
you should use the event that provide the browser and capture in the function before setState, like this
function setState = (e) =>{ //the e is the event that give you the browser
//changing the state
e.preventDefault();
}
本文标签:
版权声明:本文标题:javascript - shouldComponentUpdate equivalent for functional component, to ignore state changes - Stack Overflow 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/questions/1745531455a2154768.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论