Skip to content
Advertisement

react component not re-rendering after state change

It is rendering incorrectly when the page state changes I click on the category link and it checks if the link exists in category and then should set page state to category and likewise if I click on the recipe link it should set page state to recipe.

The value of isRecipe and isCategory are correct but it only sets the page state correctly after refreshing the page.

It should watch the page state and if this changes then it should re-render the component

How can I re-render the component if the page state changes?

const categories = [
 {name: 'first', slug: 'first'}, 
 {name: 'second', slug: 'second'},
]

const recipes = [
 {name: 'firstRecipe', slug: 'firstRecipe'},
 {name: 'secondRecipe', slug: 'secondRecipe'},
]

const Categories = ({ categories, recipe, recipes }) => {
  const router = useRouter()
  const { slug } = router.query

  const isRecipe = !!recipes.find(recipe => recipe.slug === slug)
  const isCategory = !!categories.find(cat => cat.slug === slug)

  const [page, setPage] = useState('')

  useEffect(() => {
    !!isCategory && setPage('category')
    !!isRecipe && setPage('recipe')
    }, [page])
  
  return (
    <>
      <a href="/first"> category </a>
      <a href="/firstRecipe"> recipe </a>
      {page === 'category' && <Category /> }
      {page === 'recipe' && <Recipes /> }
    </>
  )
}

export default Categories

Advertisement

Answer

The dependencies of the useEffect hook are actually isRecipe and isCategory so instead of [page], the effect should depend on [isRecipe, isCategory].

Alternately, as the helpers (isCategory and isRecipe) are derived from the slug, they could be moved into the hook and then only [slug] would be required to set the proper hook dependencies.

Note: The setPage is not required to be given to the hook dependency list as it is guaranteed to be stable by React – omitting it is safe.

Ideally, the state could be avoided entirely by a simple computation like this:

page = null

if (isRecipe) {
  page = <Recipe />
} else if (isCategory) {
  page = <Category/>
}

...

<>
  ...
  {page}
<>

There are actually three possible cases: recipe page, category page, and others that do not match the first two, which by rendering null will most likely result in what was anticipated by the code above.

Having less state, hooks, and mutations, results in less moving parts, thus most likely less bugs 🐛

Advertisement