skip to Main Content

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.

2

Answers


  1. Chosen as BEST ANSWER

    As said, Link resides in a sub-component, not the main component it routes to itself. When this.forceUpdate is called, this is referring to the sub component and only that will get "force updated". The component with 'firstImage' won't get refreshed.


  2. 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 useStateuseEffect coupling you should really just use the useMemo hook.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search