I'm trying to pass a node from a ref to a context. But because I have no re-render after the first render, the passed node is null
. I thought about two variants (but I think they aren't the best):
To pass ref
instead of ref.current
. But then in use cases, I'll be forced to use something like contextRef.current
instead of contextNode
.
Use a state (something like firstRender
) to re-render a component after getting a ref.current
. This seems not optimal.
What is a correct (the best?) way to do it?
CodeSandbox
import React, { createContext, createRef, useContext, useEffect } from "react";
import ReactDOM from "react-dom";
const Context = createContext(null);
const App = ({ children }) => {
const ref = createRef();
return (
<div ref={ref}>
<Context.Provider value={ref.current}>{children}</Context.Provider>
</div>
);
};
const Child = () => {
const contextNode = useContext(Context);
useEffect(() => {
console.log(contextNode);
});
return <div />;
};
const rootElement = document.getElementById("root");
ReactDOM.render(
<App>
<Child />
</App>,
rootElement
);
Instead of passing the ref which doesn't trigger a render when changed, use a state that holds the ref. This way you can change the Context from a child if needed, and at the same time you get the value updated correctly. ref. current will still be null in first render.
To create a ref in a functional component we use the useRef() hook which returns a mutable object with a . current property set to the initialValue we passed to the hook. This returned object will persist for the full lifetime of the component. Thus, throughout all of its re-rendering and until it unmounts.
Instead of passing the ref which doesn't trigger a render when changed, use a state that holds the ref. This way you can change the Context from a child if needed, and at the same time you get the value updated correctly.
const App = ({ children }) => {
const ref = useRef(null);
const [ref_state, setRefState] = useState(null);
useEffect(() => {
if (!ref.current) {
return;
}
setRefState(ref.current)
}, []);
return (
<div ref={ref_state}>
<Context.Provider value={ref.current}>{children}</Context.Provider>
</div>
);
};
If you need the initial render to point to the element, you could (in a non-optimal way) set the initial value to the HTML element:
const App = ({ children }) => {
const ref = useRef(document.querySelector("#app"));
return (
<div id="app" ref={ref}>
<Context.Provider value={ref.current}>{children}</Context.Provider>
</div>
);
};
I didn't know about that, but passing ref.current
doesn't work in the first render, but if you only pass ref
, it will work in the first render.
Where is the working codesandbox.
I don't think that this
then is use cases I'll be forced to use something like contextRef.current instead of contextNode.
Will be a issue, it will be good, because when using it, you will know that what you are getting is a ref.
Also,
Do this
Use a state (something like firstRender) to rerender a component after getting a ref.current. This seems not optimal.
Only for not using ref.current
, doesn't look like a good practice.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With