Open syunto07ka opened 4 years ago
Any update for this issue
Any update for this issue
unfortunately not yet ! so live with the warning without any hacking around ( should not ) ! the error must be fixed in the core of the lib not working around , so until then just ignore the warning, at least it works and nothing crashes (hopefully), i still use it in my beta project. I forgot to mention that i use snowpack and so annoyed that the package has not supported ES module, hope the author fix it soon in late of this month because Recoil is really cool !
Fix is in the v0.0.11 release so keep watch for that.
Proposed: 15-Sep-2020 (https://github.com/facebookexperimental/Recoil/issues/534#issuecomment-691580341)
I'm seeing this in 0.0.11 still. Here's a sample set of tests that are throwing it:
describe("Container", () => {
it("renders expected markup", () => {
const { container } = mount();
expect(container).toMatchSnapshot();
});
it("does not have accessibility violations", async () => {
const { container } = mount();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});
Fixed in v0.0.13 Youhouhou
v0.0.13 works for me! Excellent!!
confirmed : all fixed : the warning; useRecoilState bug with writable atom ( selector ) ; ESM support, web_modules compatible for snowpack. Thank you !
Can I see example of a test that's working for someone? I'm now on 0.0.13 and seeing the Batcher warning on this:
describe("IrrigationEditTable", () => {
it("renders correctly", () => {
let wrapper;
act(() => {
wrapper = renderedComponent();
});
expect(wrapper).toMatchSnapshot();
});
});
I am Also Facing the same warning in my console
@drarmstr @davidmccabe I can see that 0.13 is published on npm, but it's not listed under releases page here, can we see a change log?
this helped me to solve my issue.
I had a callback inside a memoed component but it wasn't using useEffect
@drarmstr @davidmccabe I can see that 0.13 is published on npm, but it's not listed under releases page here, can we see a change log?
Yes, #581 is still landing
I found this error when:
- I had a setState in parent component (e.g const [state, setState] = useState({}))
- I passed this setState to a child component and used it in a onClick={setState("value")}
- I solved this problem adding the arrow function in the onClick call
this worked for me. @DaniCastel any reason an arrow function had to be used? React newbie here
In my case, it's because I was mutating a prop value in the child component.
BEFORE
import React from 'react';
const Todo = ({ text, todo, todos, setTodos }) => {
const deleteHandler = () => {
setTodos(todos.filter(el => el.id !== todo.id)) // mutauing a prop here
}
return (
<div className="todo">
<li className="todo-item">{text}</li>
<button className="complete-btn"><i className="fas fa-check"></i></button>
<button className="trash-btn" onClick={deleteHandler()}><i className="fas fa-trash"></i></button>
</div>
);
}
export default Todo;
AFTER
import React from 'react';
const Todo = ({ text, todo, todos, setTodos }) => {
const todosCopy = JSON.parse(JSON.stringify(todos));
const deleteHandler = () => {
setTodos(todosCopy.filter(el => el.id !== todo.id)) // mutating a copy of prop value here
}
return (
<div className="todo">
<li className="todo-item">{text}</li>
<button className="complete-btn"><i className="fas fa-check"></i></button>
<button className="trash-btn" onClick={deleteHandler}><i className="fas fa-trash"></i></button>
</div>
);
}
export default Todo;
@pranavkumar389 Array.prototype.filter
doesn't mutate its original array.
Any updates at this issue?
I'm facing it in my custom IntlProvider
. In a nutshell, I'm wrapping it (from react-intl
) in a custom component that stores the selected locale in my Recoil state for further usage by my own selectors.
Here is a sample code:
import React, { ReactElement } from 'react'
import { IntlProvider } from 'react-intl'
import { OptionalIntlConfig } from 'react-intl/src/components/provider'
import { useSetRecoilState } from 'recoil'
import { locale as localeAtom } from 'state/recoil/intl/locale'
export interface RecoilIntlProviderProps extends OptionalIntlConfig {
children: ReactElement
}
const RecoilIntlProvider = (props: RecoilIntlProviderProps): ReactElement => {
const setRecoilIntl = useSetRecoilState(localeAtom)
setRecoilIntl(props.locale)
return <IntlProvider {...props} />
}
export default RecoilIntlProvider
And my atom:
import { atom } from 'recoil'
import { PREFIX } from './constants'
const KEY = `${PREFIX}::LOCALE`
export const locale = atom<string>({
key: KEY,
default: '',
})
To make it work, you need to use the following tree in your app.js(ts)
:
<RecoilRoot>
<RecoilIntlProvider locale={locale ?? 'pt-BR'} messages={messages}>
<Component {...pageProps} />
</RecoilIntlProvider>
</RecoilRoot>
And my warning in the console log:
Warning: Cannot update a component (`Batcher`) while rendering a different component (`RecoilIntlProvider`). To locate the bad setState() call inside `RecoilIntlProvider`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
RecoilIntlProvider@webpack-internal:///./components/Base/RecoilIntlProvider/recoil-intl-provider.tsx:23:87
RecoilRoot@webpack-internal:///../.yarn/$$virtual/recoil-virtual-0c2fe5134c/0/cache/recoil-npm-0.1.2-9a0edbd2b9-c69105dd7d.zip/node_modules/recoil/es/recoil.js:1722:1
BudApp@webpack-internal:///./pages/_app.tsx:70:19
ErrorBoundary@webpack-internal:///../.yarn/$$virtual/@next-react-dev-overlay-virtual-ba69454c0b/0/cache/@next-react-dev-overlay-npm-10.0.0-5db65d7be6-f38cbe7f69.zip/node_modules/@next/react-dev-overlay/lib/internal/ErrorBoundary.js:23:47
ReactDevOverlay@webpack-internal:///../.yarn/$$virtual/@next-react-dev-overlay-virtual-ba69454c0b/0/cache/@next-react-dev-overlay-npm-10.0.0-5db65d7be6-f38cbe7f69.zip/node_modules/@next/react-dev-overlay/lib/internal/ReactDevOverlay.js:73:20
Container@webpack-internal:///../.yarn/$$virtual/next-virtual-6f4174843d/0/cache/next-npm-10.0.0-82dc2f1372-c01b177cb2.zip/node_modules/next/dist/client/index.js:173:20
AppContainer@webpack-internal:///../.yarn/$$virtual/next-virtual-6f4174843d/0/cache/next-npm-10.0.0-82dc2f1372-c01b177cb2.zip/node_modules/next/dist/client/index.js:641:18
Root@webpack-internal:///../.yarn/$$virtual/next-virtual-6f4174843d/0/cache/next-npm-10.0.0-82dc2f1372-c01b177cb2.zip/node_modules/next/dist/client/index.js:757:18
Looking forward for a fix in this issue :)
I had a similar issue and it turns out the problem was how I was using it. I had an async selector which derived a value from an atom.
For example, I had a User atom and a Roles async selector. Whenever the User updated (should really only happen once) then the Roles would be loaded from the API for that User.
However, I used a hook to retrieve the User and every time the hook ran, it updated the User (in the hook), which in turn triggered a Roles API request.
Simple fix was to set the User atom from a useEffect, with the User in the deps array.
I had a similar issue just now and solved it the very same way @nickhow83 did. So in my case:
Any updates at this issue?
I'm facing it in my custom
IntlProvider
. In a nutshell, I'm wrapping it (fromreact-intl
) in a custom component that stores the selected locale in my Recoil state for further usage by my own selectors.Here is a sample code:
import React, { ReactElement } from 'react' import { IntlProvider } from 'react-intl' import { OptionalIntlConfig } from 'react-intl/src/components/provider' import { useSetRecoilState } from 'recoil' import { locale as localeAtom } from 'state/recoil/intl/locale' export interface RecoilIntlProviderProps extends OptionalIntlConfig { children: ReactElement } const RecoilIntlProvider = (props: RecoilIntlProviderProps): ReactElement => { const setRecoilIntl = useSetRecoilState(localeAtom) setRecoilIntl(props.locale) return <IntlProvider {...props} /> } export default RecoilIntlProvider
And my atom:
import { atom } from 'recoil' import { PREFIX } from './constants' const KEY = `${PREFIX}::LOCALE` export const locale = atom<string>({ key: KEY, default: '', })
To make it work, you need to use the following tree in your
app.js(ts)
:<RecoilRoot> <RecoilIntlProvider locale={locale ?? 'pt-BR'} messages={messages}> <Component {...pageProps} /> </RecoilIntlProvider> </RecoilRoot>
And my warning in the console log:
Warning: Cannot update a component (`Batcher`) while rendering a different component (`RecoilIntlProvider`). To locate the bad setState() call inside `RecoilIntlProvider`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render RecoilIntlProvider@webpack-internal:///./components/Base/RecoilIntlProvider/recoil-intl-provider.tsx:23:87 RecoilRoot@webpack-internal:///../.yarn/$$virtual/recoil-virtual-0c2fe5134c/0/cache/recoil-npm-0.1.2-9a0edbd2b9-c69105dd7d.zip/node_modules/recoil/es/recoil.js:1722:1 BudApp@webpack-internal:///./pages/_app.tsx:70:19 ErrorBoundary@webpack-internal:///../.yarn/$$virtual/@next-react-dev-overlay-virtual-ba69454c0b/0/cache/@next-react-dev-overlay-npm-10.0.0-5db65d7be6-f38cbe7f69.zip/node_modules/@next/react-dev-overlay/lib/internal/ErrorBoundary.js:23:47 ReactDevOverlay@webpack-internal:///../.yarn/$$virtual/@next-react-dev-overlay-virtual-ba69454c0b/0/cache/@next-react-dev-overlay-npm-10.0.0-5db65d7be6-f38cbe7f69.zip/node_modules/@next/react-dev-overlay/lib/internal/ReactDevOverlay.js:73:20 Container@webpack-internal:///../.yarn/$$virtual/next-virtual-6f4174843d/0/cache/next-npm-10.0.0-82dc2f1372-c01b177cb2.zip/node_modules/next/dist/client/index.js:173:20 AppContainer@webpack-internal:///../.yarn/$$virtual/next-virtual-6f4174843d/0/cache/next-npm-10.0.0-82dc2f1372-c01b177cb2.zip/node_modules/next/dist/client/index.js:641:18 Root@webpack-internal:///../.yarn/$$virtual/next-virtual-6f4174843d/0/cache/next-npm-10.0.0-82dc2f1372-c01b177cb2.zip/node_modules/next/dist/client/index.js:757:18
Looking forward for a fix in this issue :)
Your provider is setting the state in the render. If you put it in a useEffect then the problem will go away :)
It seems it’s not a problem with the library but the understanding of how it’s used in the react lifecycle.
If you had a useState for example, you wouldn’t update that directly in the render method, you’d do it inside an effect.
@syunto07ka I have the same problem, but when I run the "npm run prod" code, this error disappears. I only see this error in development mode.
@syunto07ka I have the same problem, but when I run the "npm run prod" code, this error disappears. I only see this error in development mode.
Running in prod mode only hides the errors, it still happens. Have a look at my comment above and it should explain what needs to be done
In my case the problem was with "redux" reducer file declaration where i was did some bad practice..! and after long time debug I got fix it.
switch(action.type){
case "Any_Case": return { data:action.payload, } }
Understand this issue :
If you see in above code snippet i was miss out to add in return "...state" which was the coz issue produce.
If you understand issue that if you not add "...state" in return it will whole state replace with new state and all previous state get overwrite/replace/vanish with new one state which Coz to producer issue that "Cannot update a component (xxx
) while rendering a different component (xxx
)."
So here is the solution add ...state statement in code
switch(action.type){
case "Any_Case": return { ...state, / make sure add this line of code to fix issue/ data:action.payload, } }
I'm facing this error for some months for now, no idea how to fix it?
Until now it was not causing any trouble in my application at all. And since prod "hides" those errors, it was not a big deal. But, now I'm coding some unit tests for bigger modules (using Jest), and this kind of error is causing strange behavior such as one component inside the component tree is correctly updated with the new data (after a setState from a parent component) but other component isn't.
To give more details, the component I'm trying to code has basically three parts:
While running the app, it works like a charm. It throws that Batcher error, but it works.
But, while testing it with Jest + Enzyme, it simply won't work. The first child is updated with the new state, but the second one isn't.
In the parent component I update the recoil state with the onCompleted
callback option of Apollo (but the same issue happens with useEffect
)
Here are a minimal reproduction of those 3 components:
parent:
const SettingsAccount = () => {
const myUserID = useRecoilValue(meAtom)
const setUser = useSetRecoilState(userAtomFamily(myUserID))
const [getUserData, { loading, variables }] = useLazyQuery<GetUserDataQuery>(
queries.GET_USER_DATA,
{
onCompleted: (data) => setUser(data.user),
variables: {
id: myUserID,
},
},
)
useEffect(() => {
if (myUserID && myUserID !== variables?.id) getUserData()
}, [myUserID, getUserData, variables])
return (
<Flex py={4} gridGap={6} direction="column" w="full">
<SettingsAccountHeader userID={myUserID} loading={loading} />
<Divider borderColor="black.200" />
<SettingsAccountBody userID={myUserID} loading={loading} />
</Flex>
)
}
child 1 (SettingsAccountHeader)
const SettingsAccountHeader = ({ userID, loading }: SettingsAccountHeader) => {
const user = useRecoilValue(userAtomFamily(userID))
const isLoaded = !loading && Boolean(user)
return (
<Flex gridGap={4} alignItems="center">
<UserAvatar size="xl" name={user?.fullName} src={user?.picture} />
<Flex direction="column" gridGap={4}>
<Flex direction="column" gridGap={1}>
<Skeleton isLoaded={isLoaded} {...buildSkeletonMinSize(isLoaded, 180, 24)}>
<Heading as="h2" color="black.900" fontSize="xl" fontWeight={500}>
{user?.fullName}
</Heading>
</Skeleton>
<Skeleton isLoaded={isLoaded} {...buildSkeletonMinSize(isLoaded, 220, 18)}>
<Text color="gray.400" fontSize="md" fontWeight={400}>
{user?.role}
</Text>
</Skeleton>
</Flex>
<UserTeamTags userID={userID} loading={loading} />
</Flex>
</Flex>
)
}
child 2 (SettingsAccountBody)
const SettingsAccountBody = ({ userID, loading }: SettingsAccountBodyProperties) => (
<Flex direction="column" gridGap={6}>
<SettingsAccountBodyPersonalInformations userID={userID} loading={loading} />
<Divider borderColor="black.200" />
<SettingsAccountBodySocialMedia userID={userID} loading={loading} />
</Flex>
)
first grandchild (SettingsAccountBodyPersonalInformations)
const SettingsAccountBodyPersonalInformations = ({
userID,
loading,
}: SettingsAccountBodyPersonalInformationsProperties) => {
const user = useRecoilValue(userAtomFamily(userID))
const intl = useIntl()
const isLoaded = !loading && Boolean(user)
return (
<Stack direction="column" spacing={6}>
<SettingsAccountBodySectionTitle
title={intl.formatMessage(messages.sectionTitle)}
subtitle={intl.formatMessage(messages.sectionSubtitle)}
/>
<Stack direction="column" spacing={4}>
<Flex>
<EditableField
label={intl.formatMessage(messages.firstFieldLabel)}
value={user?.firstName}
isLoaded={isLoaded}
flexGrow={1}
/>
<EditableField
label={intl.formatMessage(messages.secondFieldLabel)}
value={user?.lastName}
isLoaded={isLoaded}
flexGrow={1}
/>
</Flex>
<EditableField
label={intl.formatMessage(messages.thirdFieldLabel)}
value={user?.nickname}
isLoaded={isLoaded}
/>
<EditableField label={intl.formatMessage(messages.fourthFieldLabel)}>
<UserTeamTags userID={userID} loading={loading} />
</EditableField>
<EditableField
label={intl.formatMessage(messages.fifthFieldLabel)}
value={user?.role}
isLoaded={isLoaded}
/>
<EditableField
label={intl.formatMessage(messages.sixthFieldLabel)}
value={user?.gender}
isLoaded={isLoaded}
/>
<EditableField
label={intl.formatMessage(messages.seventhFieldLabel)}
value={user?.about}
isLoaded={isLoaded}
/>
</Stack>
</Stack>
)
}
It is a simple implementation, with just a single component changing the state (the root parent), but it still wont work. If I do console.log(user)
inside of SettingsAccountHeader
(child 1) I can get the correct data. But, if I do the same console.log(user)
inside SettingsAccountBodyPersonalInformations
(the grandchild) it prints undefined.
It does not make sense. Since both components are only reading the state (without changing it at all) why they can't print the correct results in my test environment?
Just for the record, SettingsAccountBodySocialMedia
component (the component I've not shared here, is almost the same as grandchild 1), it does not mutate state or anything.
If I get this warning once and everything else is working as expected, should I be concerned? Does it indicate a memory leak or something else that will affect the performance of the app?
If I get this warning once and everything else is working as expected, should I be concerned? Does it indicate a memory leak or something else that will affect the performance of the app?
I was in your shoes last year. I was getting this warning, and everything else was ok. My problem was when I started coding unit tests. Them, this warning was preventing my recoil state from being updated on test environments.
So, this is still problem.
I'm having this problem too!
For me it was a stupid mistake (typo). I had a memoized function, which inside of its dependencies had a Recoil setter being invoked/called, instead of just passed in the deps array.
Problematic code:
const [, setHasUnsavedChanges] = useRecoilState(hasUnsavedChangesState);
const save = useCallback(
() => {
// ...
},
[setHasUnsavedChanges()] // <--- Here's what was wrong, function was invoked, which is wrong
);
Fixed code:
const [, setHasUnsavedChanges] = useRecoilState(hasUnsavedChangesState);
const save = useCallback(
() => {
// ...
},
[setHasUnsavedChanges] // <--- Here it is fixed
);
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 Any idea on a rough timeline for when we can expect this refactor to be working?
Same here on React 17.0.2 and recoil 0.3.1.
Is it safe to ignore this warning?
Same here on React 17.0.2 and recoil 0.3.1.
Is it safe to ignore this warning?
+1
Same in React 17.0.2 and Recoil 0.3.1.
In my case, I'm using a Recoil setter inside the useMemo
hook. That should be fine, though.
I was facing same issue, The fix worked for me was if u are doing
setParams/setOptions
outside of useEffect then this issue is occurring. So try to do such things inside useEffect. It'll work like charm
I was facing same issue, The fix worked for me was if u are doing
setParams/setOptions
outside of useEffect then this issue is occurring. So try to do such things inside useEffect. It'll work like charm
For me this does not fix the problem. Actually sometimes the warning appears, sometimes not. it is weird ...
I was facing same issue, The fix worked for me was if u are doing
setParams/setOptions
outside of useEffect then this issue is occurring. So try to do such things inside useEffect. It'll work like charm
For me this does not fix the problem. Actually sometimes the warning appears, sometimes not. it is weird ...
Try to check in all screens, you might have add same code in multiple screen screens
I have the same issue. It is caused by setting pageTitle (displayed in explorer tab and history) in each component after routing using recoil. Fixed after centralizing the pageTitle logic in the root of component where pageTitle is rendered and get rid of the recoil setting action.
Same here on React 17.0.2 and recoil 0.3.1.
Is it safe to ignore this warning? @davidmccabe
+2
No, it is not safe to ignore, it is against the model of react itself.
Your component while rendering tries to alter the state of another component, this couples both of them to each other and makes the render operation of your component non atomic and with side effects impacting other components.
Recoil uses some technique internally (for batching if my memory is good enough) that alters the state of components while rendering, and react tells us not to do this, so clearly you should not ignore the message.
AFAIK, it was related to selectors, but I remember only the initial ever code about recoil, I don't honestly know how this became and if there is a chance to solve this issue.
+1 Having the same issue right now using React 18.0.25 & Recoil 0.7.6.
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