Sajjad
Sajjad

Reputation: 893

Component is not re-rendering upon url change

The Router configuration looks like following

App.js

<Router>
  <Switch>
    <Route exact path="/" render={() => <Home />} />
    <Route exact path="/products/:productId" component={ ProductDetail } />
    <Redirect to="/" />
  </Switch>
</Router>

Here is how ProductDetail component looks like

ProductDetail.js

export const ProductDetail = ({ match }) => {
  const productId = match.params.productId;
    useEffect(() => {
        dispatch(fetchProductById(productId));
  }, [productId, dispatch]);

const product = useSelector((state) => selectProductById(state, productId));
return(<.....>)
}

Now when I navigate from Home to ProductDetail then everything works fine. However if I try to hit url http://localhost:3000/products/someId directly, the component doesn't render and useEffect hook doesn't get called.

I have read the Router documentation, and it says

When you use component (instead of render or children, below) the router uses React.createElement to create a new React element from the given component.

Shouldn't that mean the newly created component(ProductDetail in my case) should render and hooks get called? I am new to React so I might be missing something fairly straightforward.

Upvotes: 0

Views: 69

Answers (1)

cbr
cbr

Reputation: 13662

As mentioned in the comments, the product will not be in the store yet when directly navigating to the ProductDetail component. The useEffect hook initiates the loading of the product, but until that finishes, product will be undefined (or null, or something else depending on your reducer). You'll need to handle this case separately.

The most simple way to handle it is to render a loading indicator of some sort, e.g.

export const ProductDetail = ({ match }) => {
  const productId = match.params.productId;
    useEffect(() => {
        dispatch(fetchProductById(productId));
  }, [productId, dispatch]);

  const product = useSelector((state) => selectProductById(state, productId));

  if (!product) {
    return <p>Loading...</p>
  }

  return(<.....>)
}

Additionally, as also mentioned in the comments, hooks in general fire when a component renders. The component does not wait for an action initiated by the useEffect hook to finish. From React's docs:

The function passed to useEffect will run after the render is committed to the screen.

Upvotes: 1

Related Questions