I solved my own problem already but I have no clue as to what's happening and why it works.
I have a Link
component that links to the same page it's currently on, but for a different product. So the same component but displaying a different product.
<Link to={directUrl} onClick={() => this.forceUpdate} state={{ urlPicture: picture }}>
Do note <Link>
is in a sub-component, not the main component it routes to itself.
Because it routes to the same page, Link
doesn't automatically refresh the page. To solve this I added onClick={() => this.forceUpdate}
, but now my state didn't pass properly. I checked this with the following lines of code in the other component.
location = useLocation()
const [ firstImage, setFirstImage ] = useState(location.state?.urlPicture || null)
console.log(`here is ${mainPicture}`) //mainPicture is just a string
However, when I replace onClick={() => this.forceUpdate}
with onClick={() => window.location.reload()}
, then my state does pass properly through the Link
.
I do know that there is a difference between the 2 and that this.forceUpdate
is the lighter of the 2, but to my knowledge it should refresh the component and update its state. I don't see why it's not sufficient and honestly I have no clue why it's working the way it does.
forceUpdate is a function, so it needs to be called in order to have any effect.
Won't work:
onClick={() => this.forceUpdate} // <-- not invoked!!
Should work:
onClick={() => this.forceUpdate()} // <-- invoked
or
onClick={this.forceUpdate} // <-- passed as click handler and invoked
This is why your onClick={() => window.location.reload()}
code does actually work, though it reloads the entire app which is very likely unwanted behavior.
This said, in almost 100% of the cases if you are reaching out for forceUpdate
to force trigger a component to rerender you are doing something incorrectly. The Link
likely is rerendering the component, but the component doesn't appear to be "reacting" to the route state change. The component being passed the route state should handle it within the React component lifecycle. In this case it would be the use of the React.useEffect
hook to re-synchronize the local component state with the passed route state.
Example:
const { state } = useLocation();
const [firstImage, setFirstImage] = useState(state?.urlPicture || null);
useEffect(() => {
if (state?.urlPicture) {
setFirstImage(state.urlPicture);
}
}, [state]);
It is, however, generally considered a bit of a React anti-pattern to store passed state/props/etc into local state. Use these values directly in the component, memoizing them if necessary.
Examples:
const { state } = useLocation();
const firstImage = state?.urlPicture || null;
const { state } = useLocation();
const firstImage = useMemo(() => state?.urlPicture || null, [state]);
In fact, just about any time you have coded a useState
-useEffect
coupling you should really just use the useMemo
hook.