Search code examples
reactjsreact-routerreact-router-domantdantd-mobile

How to call useNavigate inside useEffect? - For bottom navigation in Ant Design - Mobile


I am new to React. I am making an app using AntD-mobile. For Bottom navigation, I am making use of the tabBar Component in AntD-mobile. I was not sure how to use Link for routing with TabBar and after a lot of failed attempts, came upon useNavigate.

const [navLink, setNavLink] = React.useState("/");
const navigate = useNavigate();
const redirect = useCallback(() => navigate(navLink, {replace: true}), [navigate]);

I want to redirect to whichever tab is clicked upon, for that, I used State, TabBar's onChange changes the state first and calls redirect. But for some reason, the previous state is loaded. If I am on state 1 and click 2, it stays on 1, When I click 4, it goes to 2 and so on. So probably, the redirect is loading ahead of state change. -

<TabBar onChange={(key)=>{setNavLink(key); redirect();}}>
      {tabs.map(item => (
            <TabBar.Item key={item.key} icon={item.icon} title={item.title}>
            </TabBar.Item>
      ))}
</TabBar>

To solve, this I tried using useEffect(), wherein I change only the state in TabBar's onChange and call redirect inside useEffect. But this doesn't work. redirect is not working.

useEffect(() => {
        redirect()
    }, [navLink]);

What am I doing wrong? How to set bottom navigation in the tabBar?


Solution

  • Issue

    The original code didn't work because both setNavLink(key); and redirect(); happen in the same render cycle. When redirect is called the enqueued state update hasn't been processed yet, so it's still the previous state's value.

    onChange={(key) => {
      setNavLink(key); // <-- enqueued state update
      redirect();      // <-- navLink state not updated yet!
    }}
    

    Solutions

    The redirect function is missing a dependency, adding navLink to the dependency array will re-enclose the updated navLink state value. useCallback(() => navigate(navLink, {replace: true}), [navigate]); only computes/recomputes the callback on the initial render or when the dependencies update.

    const redirect = useCallback(
      () => navigate(navLink, { replace: true }),
      [navigate, navLink]
    );
    
    useEffect(() => {
      redirect()
    }, [redirect]);
    

    ...

    onChange={setNavLink}
    

    You can pass the navLink state in as an argument.

    const redirect = useCallback(
      (navLink) => navigate(navLink, { replace: true }),
      [navigate]
    );
    
    useEffect(() => {
      redirect(navLink);
    }, [navLink, redirect]);
    

    ...

    onChange={setNavLink}
    

    Or you can just use navigate directly in the useEffect when the navLink state updates.

    useEffect(() => {
      navigate(navLink, { replace: true });
    }, [navLink]);
    

    ...

    onChange={setNavLink}
    

    Or just navigate directly in the onChange handler.

    <TabBar onChange={(navLink) => navigate(navLink, { replace: true })}>
      {tabs.map(item => (
        <TabBar.Item
          key={item.key}
          icon={item.icon}
          title={item.title}
        />
      ))}
    </TabBar>