facebookexperimental / Recoil

Recoil is an experimental state management library for React apps. It provides several capabilities that are difficult to achieve with React alone, while being compatible with the newest features of React.
https://recoiljs.org/
MIT License
19.58k stars 1.18k forks source link

[Next.js][recoil] breaking on hydration #1652

Closed pgbradbury closed 2 years ago

pgbradbury commented 2 years ago

I have created a basic atom in a separate file as such

export const dropDownState = atom({
    key: "dropDownState",
    default: 0,
})

I am then calling this atom in a next.js react component as such

console.log("before recoil: ",dropDownState)
const dropDown = useRecoilValue(dropDownState)
console.log("after recoil: ", dropDown)

When the page renders it does so twice and I get the following output and error

before recoil:  C {key: 'dropDownState'}
after recoil:  0
before recoil:  C {key: 'dropDownState'}
react_devtools_backend.js:4061 TypeError: Cannot read properties of null (reading 'length')
    at zn (recoil.js:4476:9)
    at recoil.js:4585:58
    at rr (recoil.js:4547:10)
    at Mr (recoil.js:5315:5)
    at Ar (recoil.js:5339:20)
    at n.Z (FilterIcp.jsx:20:25)
    at oo (react-dom.production.min.js:157:137)
    at Wo (react-dom.production.min.js:180:154)
    at Ku (react-dom.production.min.js:269:343)
    at Li (react-dom.production.min.js:250:347)

The at n.Z (FilterIcp.jsx:20:25) is the line above where dropDown is assigned. It is not clear why recoil is breaking. As you can see, on the first render the atom is assigned correctly, on the second render (when the page is hydrated again) it is not and breaks on a null object, however the atom is still there and correct. as seen by the console output from the logging.

I have <RecoilRoot> in my _app.jsx

gurkerl83 commented 2 years ago

Which react version are you using, 17.0.2 or 18 experimental?

pgbradbury commented 2 years ago

"react": "^17.0.2", "recoil": "^0.6.1", "next": "^12.0.9",

gurkerl83 commented 2 years ago

I also use recoil in a Next application, have no such problems. To get to the bottom of it you should create a reproducer.

You can also look at the sources of the app to see how recoil gets integrated; just search the repository for recoil APIs.

Maybe it helps, here is the link https://github.com/project-millipede/millipede-docs

pgbradbury commented 2 years ago

Thanks for the help,

I discovered the problem I think. I was using a hook (useSession) to assign a value in a selector that was being used as the default value of an atom. I am not sure why it would cause this behaviour rather than erroring saying I was using a hook outside a react component, very difficult to track down.

Paul.