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 ofref.current
. But then in use cases, I'll be forced to use something likecontextRef.current
instead ofcontextNode
.Use a state (something like
firstRender
) to re-render a ponent after getting aref.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
);
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 ofref.current
. But then in use cases, I'll be forced to use something likecontextRef.current
instead ofcontextNode
.Use a state (something like
firstRender
) to re-render a ponent after getting aref.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
);
Share
Improve this question
edited Aug 29, 2019 at 10:39
sergdenisov
asked Aug 20, 2019 at 11:57
sergdenisovsergdenisov
8,5729 gold badges51 silver badges66 bronze badges
2 Answers
Reset to default 3Instead 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 ponent after getting a ref.current. This seems not optimal.
Only for not using ref.current
, doesn't look like a good practice.