Component renders but DOM doesn't update until next render

I solved this problem, but I can't figure out why. I had a ref in my component, and I wanted to trigger a re-render when its current value changed. (Yes, I know, this sounds like a perfect use case for state. My question is different though.) So I added a state var hasRefSet, and added a useEffect that calls setHasRefSet(true) in order to "force" re-render:
// storing my data in a ref
function Component() {
const myDataRef = useRef(null);
const [hasRefSet, setHasRefSet] = useState(false);

useEffect(() => {
setHasRefSet(true);
}, [myDataRef.current])

return (/*...*/);
}
// storing my data in a ref
function Component() {
const myDataRef = useRef(null);
const [hasRefSet, setHasRefSet] = useState(false);

useEffect(() => {
setHasRefSet(true);
}, [myDataRef.current])

return (/*...*/);
}
However, the re-render both was and wasn't happening. When I debugged my code, the component was rendering properly, and was producing the proper return JSX. Yet, the DOM never updated. On next re-render, by updating state in a parent, THEN I saw the DOM finally update. Was React saving my component's return value for later? I decided to use a state var instead, and this solved the issue:
// storing my data in state
function Component() {
const [myData, setMyData] = useState();

return (/*...*/);
}
// storing my data in state
function Component() {
const [myData, setMyData] = useState();

return (/*...*/);
}
Obviously my example is greatly simplified. I hope I've left a satisfactory amount of info. I'm not using anything fancy -- no DOM update batching, or custom reconciliation algorithm.
6 Replies
Josh
Josh8mo ago
the entire point of a reference is that it is something that is explicitly not updating the dom when myDataRef.current changes, it does not trigger a rerender. Think of rerender as your entire component code running top to bottom. useEffect is just a way to say "If data in the dep array has changed since last re-render, i should run this code" during a rerender. React only knows to re-render when calling your setXXX function from a useState hook. When a ref is updated, you not triggering a re-render, and therefore your useEffect never gets run. When a re-render DOES happen, then the useEffect will see that there is a difference and run the inner code. Putting items in the dep array does not cause them to trigger re-renders when changed I highly recomend reading the entire react documentation site front to back. I did that early on in my early days of learning react and it helped me understand what is going on under the hood a ton, and how to not shoot myself in the foot
mikey
mikey8mo ago
thanks for the response. i've read through the learn react guide front to back + did challenges, but I think it's probably time to do it again. In an effort to simplify the problem, I left out some critical info: a parent's state change is triggering re-render of Component. Then would the useEffect run if myDataRef.current had changed? i understand that state change triggers re-render, and that ref change does not. but i am trying to understand why the re-render happened anyway (which i verified it via debugging) but never ended up in the DOM. maybe my question is more of a "useEffect under the hood" one, and i'm looking into it. my understanding is, useEffect holds its own state for all its dependencies. useEffect doesn't know if a dependency comes from a prop or state or ref - they are simply values.
"If data in the dep array has changed since last re-render, i should run this code" during a rerender
just to clarify, the useEffect function runs AFTER, or as a result of, render. I think your concept of "render" in this sentence is "render + effects", yes? I'm also thinking I encountered weird behavior as a result of bad practices and I shouldn't put too effort into trying to understand it haha
Josh
Josh8mo ago
Use effect is running during the render Again, think of you render running top to bottom If the dep array of the effect doesn't change, the effect gets skipped
mikey
mikey8mo ago
the docs say "By default, Effects run after every render (including the initial one)." can't tell if you are you saying this is false, or if you are suggesting a more helpful mental model of render. https://react.dev/learn/synchronizing-with-effects#recap
Josh
Josh8mo ago
It's a much more helpful mental model to think of it as in line I actually didn't know that it technically ran after render but im not surprised
mikey
mikey8mo ago
thanks, i'll keep this in mind
Want results from more Discord servers?
Add your server
More Posts