Open james-tindal opened 1 month ago
If I understand correctly, the problem may be This issue arises from how private fields are handled in Next.js. Private fields in JavaScript classes are defined with #, and they can only be accessed by the class's own instances. In Next.js, the server-side rendering (SSR) and client-side hydration processes can lead to problems with managing these fields.
In the provided code, an error occurs during hydration when accessing an instance of a class created with useRef or useMemo. Here are some potential solutions to address this issue:
Directly Use the Class Instance You can avoid the problem by directly holding the class instance instead of using useRef.
Thanks @halilxibrahim. That is what I ended up doing.
If I understand correctly, the problem may be This issue arises from how private fields are handled in Next.js. Private fields in JavaScript classes are defined with #, and they can only be accessed by the class's own instances. In Next.js, the server-side rendering (SSR) and client-side hydration processes can lead to problems with managing these fields.
In the provided code, an error occurs during hydration when accessing an instance of a class created with useRef or useMemo. Here are some potential solutions to address this issue:
Directly Use the Class Instance You can avoid the problem by directly holding the class instance instead of using useRef.
Smart approach!
I made a hook that ensures a single class instance per component instance and allows garbage collection.
const instances: Record<string, WeakRef<WeakKey>> = {}
function add(key: string, factory: () => any) {
const oldInstance = instances[key]?.deref()
if (oldInstance)
return
const newInstance = factory()
instances[key] = new WeakRef(newInstance)
}
const get = (key: string) => instances[key]?.deref()
export const useInstance = <T extends WeakKey>(factory: () => T): T => {
const id = useId()
const initialise = () => add(id, factory)
useMemo(initialise, [])
useEffect(initialise, [])
return get(id) as T
}
Link to the code that reproduces this issue
https://codesandbox.io/p/devbox/cool-yalow-zzkp58
To Reproduce
Click preview Wait for it to hydrate It throws an error: "attempted to get private field on non-instance"
Current vs. Expected behavior
Here is the same code running in react without Next.js: https://codesandbox.io/p/sandbox/tslrlg The getter does not throw and returns the value of the private field.
In the broken Next.js version, it renders fine on the server side, but when it rehydrates it throws. The bug only happens if all of these are true:
() => new class ClassName {/* class body */}
The following function formats do not trigger the error:
() => { return new class ClassName {/* class body */} }
() => new ClassName()
() => instance
Provide environment information
Which area(s) are affected? (Select all that apply)
Runtime
Which stage(s) are affected? (Select all that apply)
next dev (local)