There's three pieces here. A state, a ref, and an effect.
A state is a value that is remembered between renders, and it re-renders it's component when changed.
A reference (or ref) is a value that persists between renders like state does, but unlike state it's not watched for any changes and can be mutated freely. Changes to refs never cause renders.
An effect is simply a function that runs after a render, or after a render in one of its dependencies has changed since the previous render.
So putting that together here's what happens:
const [count, setCount] = useState(0);
Here a state is declared. Calling setCount with a new value will cause the component to render, and when it does, count will have the value that was set.
const prevCountRef = useRef();
Then declare the ref. This will hold a reference to the previous count value. Right now it no value (undefined), which makes sense considering there is no previous value when the component first renders.
useEffect(() => {
prevCountRef.current = count;
});
Now declare an effect which runs after every render (you can tell this because it has no dependencies (would be the second argument to useEffect()). When that runs it sets the current value of prevCountRef to whatever the count is.
So, on the first render, in pseudo code:
// render starts
count // 0
prevCountRef.current // undefined
// render finishes
effect runs {
prevCountRef.current is set to 0
}
So when the render is happening the count state is 0 and the previous count ref is undefined.
Second render:
setCount(1) // triggers the second render
// second render starts
count // 1
prevCount.current // 0
// second render finishes
effect runs {
prevCount.current is set to 1
}
So as you can see, during the render you always have the current count as as state, and the previous count as the ref. This works because the effect that sets the refs value always runs after the render is done, which allows it to be whatever the previous value was.