I'm trying to set the default value for an atom in RecoilJS as the value of an asynchronous API call. I'm doing this so that when a certain React component renders it will have all of the current sets in the database without having to call useEffect(). Here's my code...
const sets = (async () => await setService.getAll())()
export const setsState = atom({
key: 'sets',
default: sets
})
Here's the error I'm getting...
index.js:1 Warning: Cannot update a component (`Batcher`) while rendering a different component (`App`). To locate the bad setState() call inside `App`, follow the stack trace as described in
in App (at src/index.js:10)
in Router (created by BrowserRouter)
in BrowserRouter (at src/index.js:9)
in RecoilRoot (at src/index.js:8)
Is there something fundamentally wrong in the approach that I am taking? Should I be accomplishing this another way? Am I doing something wrong, or is this just a broken feature of Recoil?
I'm trying to set the default value for an atom in RecoilJS as the value of an asynchronous API call. I'm doing this so that when a certain React component renders it will have all of the current sets in the database without having to call useEffect(). Here's my code...
const sets = (async () => await setService.getAll())()
export const setsState = atom({
key: 'sets',
default: sets
})
Here's the error I'm getting...
index.js:1 Warning: Cannot update a component (`Batcher`) while rendering a different component (`App`). To locate the bad setState() call inside `App`, follow the stack trace as described in https://facebook.com/setstate-in-render
in App (at src/index.js:10)
in Router (created by BrowserRouter)
in BrowserRouter (at src/index.js:9)
in RecoilRoot (at src/index.js:8)
Is there something fundamentally wrong in the approach that I am taking? Should I be accomplishing this another way? Am I doing something wrong, or is this just a broken feature of Recoil?
Share Improve this question asked Aug 4, 2020 at 22:07 jackpalaiajackpalaia 811 silver badge5 bronze badges3 Answers
Reset to default 17You can try creating a async selector and put into atom's default, like this:
const mySelector = selector({
key: 'mySelector',
get: async ({get}) => {
return await setService.getAll()
}
})
const myAtom = atom({
key:'myAtom',
default: mySelector
})
Here is a Typescript example, inspired by this answer.
// Inside your React component
const val = useRecoilValue(myAtom);
const updateVal = useSetRecoilState(mySelector);
// Update data with API call on form submit, and append new data to atom state
const onFormSubmit: SubmitHandler<DataFormValues> = async (values) => {
const createdData = await createDataWithApiCall();
await updateData([createdData]);
};
// Inside recoil state file
export const mySelector = selector({
key: 'mySelector',
get: async (): Promise<Array<Data>> => {
// Set default value to API result
const apiData = await callApi();
return apiData;
},
set: ({ set, get }, newData) => {
// Update state w/ new appended values
const currentState = get(myAtom);
const newState = [...currentState, ...newData as Data[]];
set(myAtom, newState);
},
});
export const myAtom = atom({
key: "myAtom",
default: mySelector,
});
Context: Don't be required to refetch entire to-do list from API each time a user adds an item to a to-do list. Call the API to add the new item, and append the new item to recoil state. Useful when user wants to make edits as well.
the warning you're reporting is known by the Recoil maintainers and it will be fixed in the next releases.
NOT SUGGESTED: If you want to hide it temporarily you could downgrade React to v16.12.0
, it's up to you if you could live with this warning until the next Recoil release