admin管理员组文章数量:1357291
If we set the state with the same value ponent won't re-render, but it's not applicable when I set the state in the function body.
For example, if I set the same state on the button click and the button clicked, the ponent does not re-rendering on the button click
function Test1() {
const [name, setName] = useState("Shiva");
const onButtonClick = () => {
console.log("Clicked");
setName("Shiva");
};
console.log("Redering");
return (
<div>
<span>My name is {name}</span>
<button onClick={onButtonClick}>Click Me</button>
</div>
);
}
But, when I set the same state before the return statement React goes infinite renderings
function Test2() {
const [name, setName] = useState("Shiva");
// ... e stuff
setName("Shiva");
console.log("Rendering");
return (
<div>
<span>My name is {name}</span>
</div>
);
}
What actually happening internally?
If we set the state with the same value ponent won't re-render, but it's not applicable when I set the state in the function body.
For example, if I set the same state on the button click and the button clicked, the ponent does not re-rendering on the button click
function Test1() {
const [name, setName] = useState("Shiva");
const onButtonClick = () => {
console.log("Clicked");
setName("Shiva");
};
console.log("Redering");
return (
<div>
<span>My name is {name}</span>
<button onClick={onButtonClick}>Click Me</button>
</div>
);
}
But, when I set the same state before the return statement React goes infinite renderings
function Test2() {
const [name, setName] = useState("Shiva");
// ... e stuff
setName("Shiva");
console.log("Rendering");
return (
<div>
<span>My name is {name}</span>
</div>
);
}
What actually happening internally?
Share Improve this question edited Oct 26, 2022 at 23:47 Shiva asked Oct 26, 2022 at 23:27 ShivaShiva 5822 gold badges4 silver badges18 bronze badges 10- No, I'm asking why the ponent is not re-rendered when I set the same state on the button click and why it's re-render if set before the return statement. – Shiva Commented Oct 26, 2022 at 23:34
- 2 @Shiva it does, when set state is called in the main body of the function it gets executed during the render. When the set state is defined inside the click function, that click function is just defined during the render, it's not actually executed until you click the button. So set state is not called until button is clicked, as opposed to during the render – Jayce444 Commented Oct 26, 2022 at 23:39
- hi @Jayce444, even I click on the button in the above example < Test1 /> ponent is not getting re-rendered. – Shiva Commented Oct 26, 2022 at 23:43
-
4
This is a very interesting question. I'd take a guess that it's to do with React's state tracking and where you're calling
setName()
. When you call it before the render (aka thereturn
value), React probably hasn't had a chance to track the value so it's always triggering a re-render – Phil Commented Oct 27, 2022 at 0:29 - 1 Does this answer your question? Updating state to the same state directly in the ponent body – Nick Parsons Commented Oct 29, 2022 at 4:33
2 Answers
Reset to default 8 +50React uses different approaches to schedule updates based on the place where you called your setState
.
For example, your setState
inside the event handler will use
export function enqueueConcurrentHookUpdate<S, A>(
fiber: Fiber,
queue: HookQueue<S, A>,
update: HookUpdate<S, A>,
lane: Lane,
)
While your setState
in ponent top level use
function enqueueRenderPhaseUpdate<S, A>(
queue: UpdateQueue<S, A>,
update: Update<S, A>,
)
When you call setState
, React will internally call a function,
function dispatchSetState<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
action: A,
)
If you check the declaration of this function, you will find a top level conditional check,
if (isRenderPhaseUpdate(fiber)) {
enqueueRenderPhaseUpdate(queue, update);
} else {}
Your setState
inside the event handler will use the else
block while the setState
in render phase ( top level function body ) will use if
block.
Additional
so how does React decided whether its in render phase or not ? If you check the code of the isRenderPhaseUpdate
you can see,
function isRenderPhaseUpdate(fiber: Fiber) {
const alternate = fiber.alternate;
return (
fiber === currentlyRenderingFiber ||
(alternate !== null && alternate === currentlyRenderingFiber)
);
}
Now you might heard about virtual DOM, actually its a linked list. Each object of the linked list is known as fiber nodes. these fiber nodes are nothing more than plain javascript objects. Each of these fiber nodes has a field called, alternate
.
There can be 2 separate fiber trees ( virtual doms ). One of this fiber tree is know as current tree which is the one mitted to the DOM. The other fiber tree is known as the work-in-tree. This is the one react builds newly when states updates happens yet not mitted to the DOM.
So for a given Component there can be maximum two fiber nodes ( one from current tree & another one from work-in-tree ). These two fiber nodes are connected using alternate
field.
currentlyRenderingFiber
is a global variable which keep track the currently rendering fiber node by React.
Now you should able to understand the body of above isRenderPhaseUpdate
function.
Additional Explanation Ends
CASE 01 - Event Handler
When you trigger the setState
from event handler react will use else
block of the above function.
If you check the body of the else
block you will find out following code snippet,
const currentState: S = (queue.lastRenderedState: any);
const eagerState = lastRenderedReducer(currentState, action);
update.hasEagerState = true;
update.eagerState = eagerState;
if (is(eagerState, currentState)) { // <-- checks if the previous value is equal to current one.
enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
return; // <-- return early without call `enqueueConcurrentHookUpdate`
}
// never reaches here if the `eagerState` & `currentState` are same
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
const eventTime = requestEventTime();
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
entangleTransitionUpdate(root, queue, lane);
}
As you can see when is(eagerState, currentState)
true ( Which is true in your case as both eagerState
& currentState
holds the value "Shiva"
) React will exit from the function early without calling enqueueConcurrentHookUpdate
. This is why React won't re render for the same value again.
CASE 02 - Top level of the ponent
When you call setState
from the top level of your ponent, it will run when React is traversing the ponent tree and calling your ponents ( due the execution of ponent body by React )
Notice that React is the one who call your ponents. You are just defining them. React call your ponents and get the output when traversing aka when rendering & building the new fiber tree ( aka work-in-progress tree )
Now if you check the body of
function enqueueRenderPhaseUpdate<S, A>(
queue: UpdateQueue<S, A>,
update: Update<S, A>,
) {
// This is a render phase update. Stash it in a lazily-created map of
// queue -> linked list of updates. After this render pass, we'll restart
// and apply the stashed updates on top of the work-in-progress hook.
didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
const pending = queue.pending;
if (pending === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
queue.pending = update;
}
Notice the code ment of the above function. It creates a circular linked list of schedule updates causes from the setState
in your ponent top level body.
According to the code ment, these stashed updates in the circular linked list will apply to the hook in your latest fiber node
Now notice the line in function enqueueRenderPhaseUpdate
,
didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
didScheduleRenderPhaseUpdateDuringThisPass
is a global variable. This variable is use for a while loop,
do {
didScheduleRenderPhaseUpdateDuringThisPass = false;
if (numberOfReRenders >= RE_RENDER_LIMIT) {
throw new Error(
'Too many re-renders. React limits the number of renders to prevent ' +
'an infinite loop.',
);
}
numberOfReRenders += 1;
// some other code
children = Component(props, secondArg);
} while (didScheduleRenderPhaseUpdateDuringThisPass);
Each time your ponent body get executed ( Component(props, secondArg);
in while loop), you are triggering enqueueRenderPhaseUpdate
due to the setState
in your ponent body which sets didScheduleRenderPhaseUpdateDuringThisPass
to true
which triggers the while
loop again which calls the Component
again.
Once the loop executed 25 times, React will throw an error.
You can find these function in,
\react\packages\react-reconciler\src\ReactFiberHooks.new.js
( line 2538 )\react\packages\react-reconciler\src\ReactFiberHooks.old.js
React
uses virtual DOM to update the changes into view. Virtual DOM has the rendered state when the ponent is rendered into view, that is after the execution of return
statement in a functional ponent.
Component Test1:
function Test1() {
const [name, setName] = useState("Shiva");
const onButtonClick = () => {
// State updates on button click happens after ponent gets rendered in view
setName("Shiva");
};
return (
<div>
<span>My name is {name}</span>
<button onClick={onButtonClick}>Click Me</button>
</div>
);
}
In the above example, by the time return
statement is executed, functional ponent has been rendered in view, thus React
has updated the rendered state in it's virtual DOM for tracking during uping state updates. Button click event cannot occur before the ponent gets rendered in view, thus React
is aware of rendered state, and can track changes in state to process re-rendering of ponent.
Component Test2:
function Test2() {
const [name, setName] = useState("Shiva");
// State updates before ponent gets rendered
setName("Shiva");
return (
<div>
<span>My name is {name}</span>
</div>
);
}
In the above example, state is updated before the ponent gets rendered into the view, thus React
has not yet updated it's virtual DOM with rendered state, thus there is no rendered state to track the changes. This triggers infinite re-rendering.
本文标签: javascriptWhy React goes Infinite when I set state in function bodyStack Overflow
版权声明:本文标题:javascript - Why React goes Infinite when I set state in function body? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744048610a2582003.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论