remix-run / react-router

Declarative routing for React

Home Page:https://reactrouter.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Bug]: setSearchParams is breaking Navigate behavior

Wail-Sr opened this issue · comments

What version of React Router are you using?

6.20.1

Steps to Reproduce

I'm using the useRoutes hook from 'react-router-dom' for the creation of my Router

export default function Router() {
  return useRoutes([
    {
      element: (
        <Suspense fallback={<SplashScreen />}>
          <Outlet />
        </Suspense>
      ),
      children: [
        {
          path: '/',
          element: (
            <RequireAuth allowedRoles={['Pharmacy', 'Operator']}>
              <MainLayout>
                <Outlet />
              </MainLayout>
            </RequireAuth>
          ),
          children: [
            {
              index: true,
              element: <Navigate to="/feed" replace />,
            },
            {
              path: 'feed',
              element: <FeedPage />,
            },
            {
              path: 'favorite',
              element: <FavoritePage />,
            },
          ],
        },

        { path: '*', element: <Navigate to="/404" replace /> },
      ],
    },
  ]);
}

The main.jsx file

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <HelmetProvider>
    <BrowserRouter>
        <App />
    </BrowserRouter>
  </HelmetProvider>
);

And the App.js

<ThemeProvider>
  <Router />
</ThemeProvider>

In my <MainLayout />, I have the header and footer of the pages. The header has a search bar. When the app starts, I use useEffect to check if there are any search parameters in the query, and if there are, I fill in the search bar with them.

  const [searchParams, setSearchParams] = useSearchParams();
  const location = useLocation();
  const queryParams = {};

  useEffect(() => {
    setSearchKeyword(searchParams.get('q') || '');
  }, [location.search]);

  const handleFetchData = useCallback(() => {
    const queryParams = new URLSearchParams(searchParams);
    if (searchKeyword) {
      queryParams.set('q', searchKeyword);
    } else {
      queryParams.delete('q');
    }
    setSearchParams(queryParams.toString());              /////// here is the issue
  }, [searchKeyword]);

  const debouncedFetch = useCallback(debounce(handleFetchData, 500), [handleFetchData]);
  useEffect(() => {
      debouncedFetch();
      return () => {
        debouncedFetch.cancel();
      };
  }, [debouncedFetch]);

When you open the application at '/', it should automatically redirect to the feed page. However, it doesn't do that because of how setSearchParams is used in the handleFetchData function to retrieve the search query if it exists. Here's what's happening: it goes to '/', redirects to '/feed', then immediately returns to '/', resulting in a blank page (except for the header and the footer).

My theory is that when you first visit '/', setSearchParams captures the URL and attempts to extract the search parameters from it. But because JavaScript works asynchronously, it doesn't wait for this process to finish before moving on, so it quickly redirects to the '/feed' page. Since setSearchParams doesn't find any search terms, it just goes back to the original page ('/')

Expected Behavior

Automatically redirect to the feed page.

Actual Behavior

Redirect to the feed page, and instantly return to the '/' page. And stucks there.

I'm observing a similar issue. Redirect with index route + <Navigate/> works as before on the version 6.11.0 and breaks on 6.11.1. Looks like this might caused the change in behaviour.

Update:
I've implemented custom "Navigate" component with navigate() inside useEffect and condition for data router dataRouterState.navigation.state !== "idle" and it fixed my case.

Update 2: only redirect is fixed but not the setSearchParams.

Update 3: according to this comment redirect should be performed with loader if data router is used.

Would you mind creating a working reproduction in codesandbox or stackblitz we can use to triage?

This issue has been automatically closed because we haven't received a response from the original author 🙈. This automation helps keep the issue tracker clean from issues that aren't actionable. Please reach out if you have more information for us! 🙂