Recoil
Recoil copied to clipboard
Warning: Cannot update a component (`xxx`) while rendering a different component (`xxx`).
A warning is occured where useRecoilValue() is used even though I write a code like one which is in the official document here.

this is my code
import React, { useEffect } from 'react';
import { atom, useRecoilState, useRecoilValue, selector } from 'recoil';
function RecoilApp() {
useEffect(() => {
console.log('test');
});
return(
<div>
Recoil App
<TextInput />
<CharCount />
</div>
);
}
const textState = atom({key: 'textState', default: ''});
function TextInput() {
const [text, setText] = useRecoilState(textState);
const onChange = event => {
setText(event.target.value);
}
return(
<div>
<input type="text" value={text} onChange={onChange} />
<br />
Echo: {text}
</div>
);
};
function CharCount() {
const count = useRecoilValue(countState);
return <>Charset Count: {count}</>;
}
const countState = selector({
key: 'countState',
get: ({get}) => {
const text = get(textState);
return text.length;
},
});
export default RecoilApp;
I have copied and pasted your code and it works. See example in codesandbox
Thanks, ecreeth. As you say that, my code works, but warning is occured in my browser.
Humm, I wonder that the codesandbox occured no warning at console like the image.
Humm, I wonder that the codesandbox occured no warning at console like the image.
You need to try with react v16.13.1. https://codesandbox.io/s/async-pine-gogdb
(edit: I'm surprised to get so many down votes. Many people probably misunderstood my comment is how to fix the warning. My apologies for not clarifying the context. It was a comment for @syunto07ka to "reproduce" the warning in codesandbox.)

The error is present in both the codesandbox links
it's my installed dependences in pakcage.json. so yeah, "16.13.1" is correct.
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1",
"recoil": "0.0.7"
},
Thanks for reporting, I am investigating this.
Maybe something related to hot-loader? I just had:
Warning: Cannot update a component from inside the function body of a different component.
then I removed the react-dom alias from my webpack config
alias: {
'react-dom': '@hot-loader/react-dom',
},
and the error changes to
Warning: Cannot update a component (`Batcher`) while rendering a different component (`Header`). To locate the bad setState() call inside `Header`, follow the stack trace as described in https://fb.me/setstate-in-render
Header is my component name...
Maybe something related to hot-loader? I just had:
Warning: Cannot update a component from inside the function body of a different component.
then I removed the react-dom alias from my webpack config
alias: { 'react-dom': '@hot-loader/react-dom', },
and the error changes to
Warning: Cannot update a component (`Batcher`) while rendering a different component (`Header`). To locate the bad setState() call inside `Header`, follow the stack trace as described in https://fb.me/setstate-in-render
Header is my component name...
I noticed also that setters stop working when hot reloading is set ... so having this related as well sounds like something worth exploring
Yes, we have a known issue with hot module loading at the moment.
Having the same issue. Thanks for reporting @syunto07ka and thanks for investigating @davidmccabe.
I have this issue and I'm not using react hot module loading.
The Batcher
's component's setState
is the state setter that is being called, and the main cause is because replaceState
from RecoilRoot
is called when rendering subscribed components.
The warning was initially added to react on the v16.13.0
release, and highlights an anti-pattern of the react mental model.
Inlining the logic in the batcher's effect to replaceState
solves the issue (along with setTimeout
on the notifyBatcherOfChange.current()
, but I don't believe that setTimeout
is a reliable solution). While RecoilRoot
wraps a context provider, a state inside it could be a solution, but we will return to the initial problem.
I understand that the main goal of the Batcher
is to catch as many setState
calls from subscribers as possible and delegate to react batching to gather queuedComponentCallbacks
and optimize the count of re-renders.
Moreover, the subscribed component callback is the forceUpdate
(state setter) of useInterface
that is linked to subscribed components. So if many updates occur in a short duration, I prefer leaving react to handle the batching stuff rather than building a logic on top of it, and also because it is unstable.
Besides, the replaceState
will notify the batcher only with the new state reference change, and as far as I read, this is the case when a new subscription occurs.
My question is, Shouldn't we drop the batcher and leave react manage the batching of state updates ? I mean, it is like an abstraction over an abstraction, or am I mistaking something?
I think to solve the problem without dropping the Batcher
, a work loop that schedules when to flush the subscriptions is mandatory (may have its own problems though).
What if Batcher itself was rendered whenever a selector was rendered. eg, treat it as if it depends on all selectors. Then it would automatically queue its useEffect, and you would not need notify the Batcher of a change in replaceState, Batcher could test for the change when running it's useEffect. I think this also solves the "double render" whenever a selector changes dependencies.
I see now that replaceState is called under other conditions that are not part of a render.
This particular issue occurs because:
- selectors are run during render
- selectors update their dependencies when they run
- updating dependencies calls replaceState
- replaceState notifies the Batcher through use of setState on the Batcher, which happens during the render of the component that uses the selector
I'm working on a big refactor where dependencies will not go through React state at all. That will fix this (and make it a lot more efficient, and is needed for CM). In the meantime I don't see a an obvious workaround unfortunately.
@davidmccabe - that would be great - pls post any updates when you have an ETA. Thanks again.
I am getting the same error, In my case this error pops up if I use the selector variable as a dependency in my component's useEffect For eg: in recoil:
// get decoded URL of meetingId
export const getDecodedMeetingId = selector ({
key: 'decodedMeetingIdState',
get: ({get}: any) => {
const meetingId = get(meetingIdState);
return decodeURI(meetingId);
}
});
And then my component useEffect
const meetingStatus = useRecoilValue(getDecodedMeetingId);
// side-effects
useEffect(()=>{
if (meetingStatus) {
console.log('Begin Timer');
startTimer();
} else {
setTimer(prev => prev);
}
},[meetingStatus]);
Hopefully this helps. If I am doing something wrong, please let me know.
@davidmccabe I'm very curious - can you give any technical details on what you're doing and how it will improve CM compat? My general understanding was that any non-React-based state management approach will have issues with CM, because it can't tie in to React's ability to reorder state updates based on render priorities.
@markerikson That was my misunderstanding too, because they said at some point about considering the use of useMutableSource as one option.
https://github.com/facebookexperimental/Recoil/issues/5#issuecomment-628796942
In Recoil, state seems React based.
Hi, any ETA on this fix ? https://github.com/facebookexperimental/Recoil/issues/12#issuecomment-631201906 This is the problem, I am facing when using recoil... https://github.com/facebookexperimental/Recoil/issues/12#issuecomment-631238293
Confirming Recoil breaking hmr/fast refresh.
Hi, i found this comment from react-final-form where they had a similar issue and solved it: https://github.com/final-form/react-final-form/issues/751#issuecomment-606212893
Hope this helps
I have the same issue
I think replaceState shouldn't cause Batcher update when we call getRecoilValueAsLoadable, since getRecoilValueAsLoadable just get value from RecoilNode for computing derived value. I think the following code would work, But I am not for sure. @davidmccabe
const replaceState = (replacer, shouldNotNotifyBatcher) => {
\\ other code
if (shouldNotifyBatcher === true) return;
nullthrows(notifyBatcherOfChange.current)();
};
function getRecoilValueAsLoadable<T>(
store: Store,
{key}: AbstractRecoilValue<T>,
): Loadable<T> {
let result: Loadable<T>;
// Save any state changes during read, such as validating atoms,
// updated selector subscriptions/dependencies, &c.
Tracing.trace('get RecoilValue', key, () =>
store.replaceState(
Tracing.wrap(state => {
const [newState, loadable] = getNodeLoadable(store, state, key);
result = loadable;
return newState;
}),
true
),
);
return (result: any); // flowlint-line unclear-type:off
}
Same issue.
Hi @davidmccabe, Any ETA on this?
Inclusion of any selector
in any FC whereby the selector calls get
on an atom, generates this error. The simplest use case in my project is:
//
// foobarState.ts
//
export const foo = atom<boolean>({ key: "foo", default: true });
export const bar = selector<boolean>({ key: "bar", get: ({ get }) => !get(foo) });
//
// component.tsx
//
...
export default ((props) => {
const foobar = useRecoilValue(bar);
return <>Hello World</>
}
As an early adopter with high hopes for this project, I'm migrating away from Redux to Recoil!! Crossing fingers to see Recoil "stable enough" for our beta in a few months.
Thanks for your efforts on this!!
I got his warning because I used a selector get another async selector get. Some example code
export const asyncSelector = selector>({
key: 'AsyncSelector ',
get: async ({ get }) => {
const response = await fakeData();
const data = await response.json();
await new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 200);
});
return data
},
});
export const getAsyncSelector= selectorFamily({
key: 'GetAsyncSelector',
get: (params) => ({ get }) => {
return get(asyncSelector ).slice(params);
},
});
Also encountered this warning today, decided to step back to [email protected]
and [email protected]
in the meantime. Thanks guys for your work, this library looks very promising!
Any update for this issue :( i try to add this to new project instead of redux ...
// store.js
export const todoListTypeState = atom({
key: "todoListTypeState",
default: "snack",
});
export const todoListState = atom({
key: "todoListState",
default: [
{ type: "snack", content: "cake" },
{ type: "drink", content: "water" },
],
});
export const todoListSelector = selector({
key: "todoListSelector",
get: ({ get }) => {
const type = get(todoListTypeState);
const todoList = get(todoListState);
return todoList.filter((item) => item.type === type);
},
set: ({ set }, newValue) => {
set(todoListState, newValue);
},
});
// Todo.js
const todoList = useRecoilValue(todoListState); // :} good
const [typedTodoList, setTodoList] = useRecoilState(todoListSelector); // :{ warning
waitting...