origin
React
Re-rendering means that in a class function, it will be executed again.render
Function, similarFlutter
In-housebuild
Function, function component, will be executed again
React
Components are in component statestate
or component propertiesprops
When changes are made, they will be re-rendered. The conditions are simple, but in fact, if you are not careful, it will cause disastrous re-rendering.
Class Components
Why talk about the class components first, how to talk about them, and better understand them? There are also some common interview questions that were popular in the past few years
React
In-housesetState
When is it synchronous and when is it asynchronous
React
setState
How to get the lateststate
What is the output value of the following code and how the page display changes
test = () => { // s1 = 1 const { s1 } = ; ({ s1: s1 + 1}); ({ s1: s1 + 1}); ({ s1: s1 + 1}); (s1) }; render() { return ( <div> <button onClick={}>Button</button> <div>{.s1}</div> </div> ); }
See these types of interview questions and be familiar with themReact
You can definitely answer the transaction mechanism, after all, isn't it difficult, ha? you do not knowReact
Transaction mechanism? Baidu|Google|360|Sogou|Bing React Transaction Mechanism
React synthesis events
existReact
The event triggered by the component will be bubbleddocument
(existreact v17
Medium Yesreact
The mounted node, for example ('#app')), thenReact
Collect event callbacks on the trigger path and distribute events.
- Is this a sudden idea? If it is disabled, the event will be prohibited from bubbled through the native event at the node that triggers the event. Is it?
React
The event cannot be triggered? That's true, I can't bubbling.React
It is impossible to collect and distribute events, please note that this bubbling is notReact
The bubbling of synthetic events. - Another point that can be imagined after diverging,
React
Even if the event triggered during the synthesis capture stage is still triggered after the native bubble event is triggered
reactEventCallback = () => { // s1 s2 s3 are all 1 const { s1, s2, s3 } = ; ({ s1: s1 + 1 }); ({ s2: s2 + 1 }); ({ s3: s3 + 1 }); ('after setState s1:', .s1); // 1 is still output here, page display 2, page is only re-rendered once}; <button onClick={} onClickCapture={} > React Event </button> <div> S1: {s1} S2: {s2} S3: {s3} </div>
Triggered setState after the timer callback
Timer callback executionsetState
It is synchronous and can be executedsetState
Then, get the latest value directly, such as the following code
timerCallback = () => { setTimeout(() => { // s1 s2 s3 are all 1 const { s1, s2, s3 } = ; ({ s1: s1 + 1 }); ('after setState s1:', .s1); // Output 2 page rendering 3 times ({ s2: s2 + 1 }); ({ s3: s3 + 1 }); }); };
Asynchronous function post-key trigger setState
Asynchronous function callback executionsetState
It is synchronous and can be executedsetState
Then, get the latest value directly, such as the following code
asyncCallback = () => { ().then(() => { // s1 s2 s3 are all 1 const { s1, s2, s3 } = ; ({ s1: s1 + 1 }); ('after setState s1:', .s1); // Output 2 page rendering 3 times ({ s2: s2 + 1 }); ({ s3: s3 + 1 }); }); };
Native event triggers
Native events are also unacceptableReact
Transaction mechanism affects, sosetState
Performance is also synchronous
componentDidMount() { const btn1 = ('native-event'); btn1?.addEventListener('click', ); } nativeCallback = () => { // s1 s2 s3 are all 1 const { s1, s2, s3 } = ; ({ s1: s1 + 1 }); ('after setState s1:', .s1); // Output 2 page rendering 3 times ({ s2: s2 + 1 }); ({ s3: s3 + 1 }); }; <button >Native Event</button>
setState Modify properties that do not participate in rendering
setState
The call will cause the component to be re-rendered, even if this state does not participate in page rendering, so please do not put non-rendering attributes.state
Even if it is putstate
, please do not passsetState
Modify this state and call it directly = xxx
That's fine, this property that does not participate in rendering is directly hung inthis
Just above, refer to the picture below
// s1 s2 s3 is the rendered property, s4 is the non-rendered propertystate = { s1: 1, s2: 1, s3: 1, s4: 1, }; s5 = 1; changeNotUsedState = () => { const { s4 } = ; ({ s4: s4 + 1 }); // The page will be re-rendered // The page will not be rerendered .s4 = 2; this.s5 = 2; }; <div> S1: {s1} S2: {s2} S3: {s3} </div>;
Just call setState, will the page be re-rendered?
Several situations are:
- Call directly
setState
, no parameters -
setState
,newstate
And oldstate
Completely consistent, that is, the samestate
sameState = () => { const { s1 } = ; ({ s1 }); // The page will be re-rendered}; noParams = () => { ({}); // The page will be re-rendered};
These two situations are handled with ordinary modification of the statesetState
Consistent will cause re-rendering
Multiple rendering issues
Why do you need to mention the above? Look carefully. Here are many renderings.3
Time, it is more suitable for our daily code writing, asynchronous function callbacks. After all, in timer callbacks or binding native events to components (it's okay to find trouble, right?), there are quite a few, but there are many asynchronous callbacks, such as network requests, change them.state
It's quite common, but it's not possible to render it many times! But usesetState
In fact, it is a new object merging mechanism that can merge the changed attributes into the new object and submit all changes at once, without calling them multiple times.setState
It's
asyncCallbackMerge = () => { ().then(() => { const { s1, s2, s3 } = ; ({ s1: s1 + 1, s2: s2 + 1, s3: s3 + 1 }); ('after setState s1:', .s1); // Output 2 page rendering once }); };
This way it can beReact
Avoid multiple rendering issues in transaction flow
Test code
import React from 'react'; interface State { s1: number; s2: number; s3: number; s4: number; } // eslint-disable-next-line @iceworks/best-practices/recommend-functional-component export default class TestClass extends <any, State> { renderTime: number; constructor(props: any) { super(props); = 0; = { s1: 1, s2: 1, s3: 1, s4: 1, }; } componentDidMount() { const btn1 = ('native-event'); const btn2 = ('native-event-async'); btn1?.addEventListener('click', ); btn2?.addEventListener('click', ); } changeNotUsedState = () => { const { s4 } = ; ({ s4: s4 + 1 }); }; reactEventCallback = () => { const { s1, s2, s3 } = ; ({ s1: s1 + 1 }); ({ s2: s2 + 1 }); ({ s3: s3 + 1 }); ('after setState s1:', .s1); }; timerCallback = () => { setTimeout(() => { const { s1, s2, s3 } = ; ({ s1: s1 + 1 }); ('after setState s1:', .s1); ({ s2: s2 + 1 }); ({ s3: s3 + 1 }); }); }; asyncCallback = () => { ().then(() => { const { s1, s2, s3 } = ; ({ s1: s1 + 1 }); ('after setState s1:', .s1); ({ s2: s2 + 1 }); ({ s3: s3 + 1 }); }); }; nativeCallback = () => { const { s1, s2, s3 } = ; ({ s1: s1 + 1 }); ('after setState s1:', .s1); ({ s2: s2 + 1 }); ({ s3: s3 + 1 }); }; timerCallbackMerge = () => { setTimeout(() => { const { s1, s2, s3 } = ; ({ s1: s1 + 1, s2: s2 + 1, s3: s3 + 1 }); ('after setState s1:', .s1); }); }; asyncCallbackMerge = () => { ().then(() => { const { s1, s2, s3 } = ; ({ s1: s1 + 1, s2: s2 + 1, s3: s3 + 1 }); ('after setState s1:', .s1); }); }; nativeCallbackMerge = () => { const { s1, s2, s3 } = ; ({ s1: s1 + 1, s2: s2 + 1, s3: s3 + 1 }); ('after setState s1:', .s1); }; sameState = () => { const { s1, s2, s3 } = ; ({ s1 }); ({ s2 }); ({ s3 }); ('after setState s1:', .s1); }; withoutParams = () => { ({}); }; render() { ('renderTime', ++); const { s1, s2, s3 } = ; return ( <div className="test"> <button onClick={}>React Event</button> <button onClick={}>Timer Callback</button> <button onClick={}>Async Callback</button> <button >Native Event</button> <button onClick={}>Timer Callback Merge</button> <button onClick={}>Async Callback Merge</button> <button >Native Event Merge</button> <button onClick={}>Change Not Used State</button> <button onClick={}>React Event Set Same State</button> <button onClick={}> React Event SetState Without Params </button> <div> S1: {s1} S2: {s2} S3: {s3} </div> </div> ); } }
Function Components
The conditions for re-rendering of function components are the same as those of class components, and the properties of components areProps
and component statusState
When there are modifications, the component will be re-rendered, so the problem with class components also exists, and because of the function componentsstate
It's worse if it's not a person
React synthesis events
const reactEventCallback = () => { // S1 S2 S3 are all 1 setS1((i) => i + 1); setS2((i) => i + 1); setS3((i) => i + 1); // The page will only be rendered once, S1 S2 S3 are all 2};
Timer callback
const timerCallback = () => { setTimeout(() => { // S1 S2 S3 are all 1 setS1((i) => i + 1); setS2((i) => i + 1); setS3((i) => i + 1); // The page will only be rendered three times, S1 S2 S3 are all 2 }); };
Asynchronous function callback
const asyncCallback = () => { ().then(() => { // S1 S2 S3 are all 1 setS1((i) => i + 1); setS2((i) => i + 1); setS3((i) => i + 1); // The page will only be rendered three times, S1 S2 S3 are all 2 }); };
Native events
useEffect(() => { const handler = () => { // S1 S2 S3 are all 1 setS1((i) => i + 1); setS2((i) => i + 1); setS3((i) => i + 1); // The page will only be rendered three times, S1 S2 S3 are all 2 }; ?.addEventListener('click', handler); return () => ?.removeEventListener('click', handler); }, []);
Update unused status
const [s4, setS4] = useState<number>(1); const unuseState = () => { setS4((s) => s + 1); // s4 === 2 The page is rendered once. S4 page is not used};
summary
All of the above situations areReact Hook
The performance of the class component is exactly the same as that of the performance of the class component, and there is no difference, but there are also differences in the performance of the performance.
Different situations Set the same State
existReact Hook
Set the sameState
, will not cause re-rendering, this is different from class components, but this is not necessarily the case.React
Official documentation statement
If the state after you update the State Hook is the same as the current state, React will skip the rendering of the child component and will not trigger the execution of the effect. (React uses the comparison algorithm to compare state.)
It should be noted that React may still need to render the component before skipping rendering. However, since React does not render unnecessary "deep" nodes of the component tree, there is no need to worry. If you perform high overhead calculations during rendering, you can use useMemo to optimize.
Official stability mentioned, new and oldState
The shallower is completely consistent and will not be re-rendered, but it may still lead to re-rendering.
// React Hook const sameState = () => { setS1((i) => i); setS2((i) => i); setS3((i) => i); (); // The page will not be rerendered}; // In the class componentsameState = () => { const { s1, s2, s3 } = ; ({ s1 }); ({ s2 }); ({ s3 }); ('after setState s1:', .s1); // The page will be re-rendered};
This feature exists, sometimes you want to get the latest onestate
, don't want to add a functionstate
Depend on or givestate
Add oneuseRef
, you can use this function or thisstate
Latest value
const sameState = () => { setS1((i) => { const latestS1 = i; // latestS1 is the latest value of S1, and you can handle some S1-related logic here return latestS1; }); };
Avoid multiple renderings in React Hook
React Hook
middlestate
It is not an object, so the object will not be automatically merged and updated. How to solve this asynchronous function multiple times aftersetState
Re-rendering problem?
Merge all states into one object
const [state, setState] = useState({ s1: 1, s2: 1, s3: 1 }); setState((prevState) => { setTimeout(() => { const { s1, s2, s3 } = prevState; return { ...prevState, s1: s1 + 1, s2: s2 + 1, s3: s3 + 1 }; }); });
Reference classIt is an object method, to put all
state
When merged into a component and then update a certain property, call it directlysetState
That is, it is exactly the same as the operation of the class component. This is a solution
Use useReducer
Although thishook
It is indeed low in presence, but multi-state components are replaced by thisuseState
It's really good
const initialState = { s1: 1, s2: 1, s3: 1 }; function reducer(state, action) { switch () { case 'update': return { s1: state.s1 + 1, s2: state.s2 + 1, s3: state.s3 + 1 }; default: return state; } } const [reducerState, dispatch] = useReducer(reducer, initialState); const reducerDispatch = () => { setTimeout(() => { dispatch({ type: 'update' }); }); };
The specific usage will not be expanded, andredux
No big difference
The status is declared directly with Ref, and the updated function is called when it needs to be updated (not recommended)
// S4 does not participate in renderingconst [s4, setS4] = useState<number>(1); // update is the dispatch of useReducer. The page is updated by calling it, which is much better than defining a state that does not render.const [, update] = useReducer((c) => c + 1, 0); const state1Ref = useRef(1); const state2Ref = useRef(1); const unRefSetState = () => { // Priority update of the value of ref += 1; += 1; setS4((i) => i + 1); }; const unRefSetState = () => { // Priority update of the value of ref += 1; += 1; update(); }; <div> state1Ref: {} state2Ref: {} </div>;
Do this to render thestate
Put it hereref
Inside, there is an advantage that this does not need to be declared in the function.state
Reliance, but there are many disadvantages. You must call it when updatingupdate
, and at the same timeref
It's also strange to render
Custom Hook
CustomizeHook
If used in a component, any customizationHook
Changes in the state will cause the component to be re-rendered, including those not used in the component, but are defined in customHook
Status in
Simple example, the following customizationhook
,haveid
anddata
Two states,id
Not even exported, butid
When it changes, it will still cause this referenceHook
Re-rendering of components
// A simple custom Hook for requesting dataconst useDate = () => { const [id, setid] = useState<number>(0); const [data, setData] = useState<any>(null); useEffect(() => { fetch('The URL of the request data') .then((r) => ()) .then((r) => { // Re-render the component setid((i) => i + 1); // The component is re-rendered again setData(r); }); }, []); return data; }; // Used in components, even if only data is exported, the id changes will also cause the component to be re-rendered. Therefore, when the component obtains data, the component will re-render twiceconst data = useDate();
Test code
// const useDate = () => { const [id, setid] = useState<number>(0); const [data, setData] = useState<any>(null); useEffect(() => { fetch('Data request address') .then((r) => ()) .then((r) => { setid((i) => i + 1); setData(r); }); }, []); return data; }; import { useEffect, useReducer, useRef, useState } from 'react'; import useDate from './use-data'; const initialState = { s1: 1, s2: 1, s3: 1 }; function reducer(state, action) { switch () { case 'update': return { s1: state.s1 + 1, s2: state.s2 + 1, s3: state.s3 + 1 }; default: return state; } } const TestHook = () => { const renderTimeRef = useRef<number>(0); const [s1, setS1] = useState<number>(1); const [s2, setS2] = useState<number>(1); const [s3, setS3] = useState<number>(1); const [s4, setS4] = useState<number>(1); const [, update] = useReducer((c) => c + 1, 0); const state1Ref = useRef(1); const state2Ref = useRef(1); const data = useDate(); const [state, setState] = useState({ s1: 1, s2: 1, s3: 1 }); const [reducerState, dispatch] = useReducer(reducer, initialState); const containerRef = useRef<HTMLButtonElement>(null); const reactEventCallback = () => { setS1((i) => i + 1); setS2((i) => i + 1); setS3((i) => i + 1); }; const timerCallback = () => { setTimeout(() => { setS1((i) => i + 1); setS2((i) => i + 1); setS3((i) => i + 1); }); }; const asyncCallback = () => { ().then(() => { setS1((i) => i + 1); setS2((i) => i + 1); setS3((i) => i + 1); }); }; const unuseState = () => { setS4((i) => i + 1); }; const unRefSetState = () => { += 1; += 1; setS4((i) => i + 1); }; const unRefReducer = () => { += 1; += 1; update(); }; const sameState = () => { setS1((i) => i); setS2((i) => i); setS3((i) => i); (); }; const mergeObjectSetState = () => { setTimeout(() => { setState((prevState) => { const { s1: prevS1, s2: prevS2, s3: prevS3 } = prevState; return { ...prevState, s1: prevS1 + 1, s2: prevS2 + 1, s3: prevS3 + 1 }; }); }); }; const reducerDispatch = () => { setTimeout(() => { dispatch({ type: 'update' }); }); }; useEffect(() => { const handler = () => { setS1((i) => i + 1); setS2((i) => i + 1); setS3((i) => i + 1); }; ?.addEventListener('click', handler); return () => ?.removeEventListener('click', handler); }, []); ('render Time Hook', ++); ('data', data); return ( <div className="test"> <button onClick={reactEventCallback}>React Event</button> <button onClick={timerCallback}>Timer Callback</button> <button onClick={asyncCallback}>Async Callback</button> <button ref={containerRef}> Native Event </button> <button onClick={unuseState}>Unuse State</button> <button onClick={sameState}>Same State</button> <button onClick={mergeObjectSetState}>Merge State Into an Object</button> <button onClick={reducerDispatch}>Reducer Dispatch</button> <button onClick={unRefSetState}>useRef As State With useState</button> <button onClick={unRefSetState}>useRef As State With useReducer</button> <div> S1: {s1} S2: {s2} S3: {s3} </div> <div> Merge Object S1: {state.s1} S2: {state.s2} S3: {state.s3} </div> <div> reducerState Object S1: {reducerState.s1} S2: {reducerState.s2} S3:{' '} {reducerState.s3} </div> <div> state1Ref: {} state2Ref: {} </div> </div> ); }; export default TestHook;
What should I do if I can’t remember the rules?
There are a lot of situations listed above, but these rules are inevitable.React
The two completely different re-rendering mechanisms caused by the transaction mechanism really make people feel a little disgusting.React
The official also noticed that since it is in the transaction flowsetState
Can merge, that's notReact
Can the callback of the transaction flow be merged? The answer is OK.React
The official is actuallyReact V18
middle,setState
Can merge, even in asynchronous callbacks, timer callbacks, or native event binding, the test code can be dropped directlyReact V18
Try in the environment, even if the scene listed above will render multiple times, it will not be re-rendered multiple times.
You can see this address for details
Automatic batching for fewer renders in React 18
But,React V18
It is best to record the above rules, which are very helpful for reducing the number of renderings.
The above is the detailed content of the re-rendering class components and function components in React. For more information about React re-rendering components, please follow my other related articles!