Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: redirect after adding a new record automatically redirects back to the original path #12134

Open
MChez opened this issue Oct 16, 2024 · 3 comments
Labels

Comments

@MChez
Copy link

MChez commented Oct 16, 2024

What version of React Router are you using?

6.26.2

Steps to Reproduce

Redirect after adding a new record automatically redirects back to the original path

Overview

I am using a pattern where the form to edit an entry is the same form used to create new entries.

When a user views an existing entry, the path contains the entry id which is greater than zero. when a user edits an existing entry, they are redirected back to the same entry with query parameters that allow me to display a "data saved" message.

When a new entry is created the path contains the entry id 0 (zero). When the new entry is saved to the API, the user is redirected back to the now existing entry with the new entry's new id in the path.

The route is:

  const router = createBrowserRouter([
    {
      path: "/",
      element: <Root />,
      errorElement: <ErrorPage />,
      children: [
        {
          // This route has no path, which lets it participate in the UI layout without requiring new path segments in the URL.
          errorElement: <ErrorPage />,
          children: [
            {
              // Tells the router to match and render this route when the user is at the parent route's exact path.
              index: true,
              element: <HqList />,
              loader: getHqsLoader,
            },
            {
              path: ":hqId",
              element: <HqTabLayout />,
              loader: getHqLoader,
              children: [
                {
                  index: true,
                  element: <General />,
                  loader: getHqLoader,
                  action: actionHqSave,
                },
                {
                  path: "pricing",
                  element: <Pricing />,
                },
              ],
            },
          ],
        },
      ],
    },
  ]);

actionHqSave either sends a response object back to the form with an error message or redirects the user back to the same path with query parameters saved and timestamp. The response form the API contains either the id of the item that was created or edited or an error message.

Snippet from actionHqSave:

  if (apiResponse.status === 200) {
    const now = new Date();
    const newId = apiResponse.data as number;
    return redirect(`/${newId}?saved&timestamp=${now.toISOString()}`);
  } else {
    return apiResponse;
  }

timestamp is used to ensure that the page refreshes and further edits can be saved. saved is used to trigger a "data saved" message to give the user some feedback.

A list of items are displayed in a table at the root (/) path. The user may view/edit an existing item or use a button to start creating a new item.

View/Edit Existing Item

A user selects an item from the table and is redirected to a form that displays the details of the item (path e.g. /1) and allows the item to be edited. There is a heading that displays the item's name with a back button and tabs that allow the user to switch to other paths (path e.g. /1/pricing) that can show other forms with additional information about the item.

Create New Item

A user may select a button and is redirected to the same form but loaded with an empty default item object (path e.g. /0). There is a heading that displays "New" as the item's name with a back button and tabs, but the other tabs are disabled because no additional information currently exists for the item.

Sandbox Example

I've created a sandbox example to help reproduce the behaviour: Sandbox Example

  1. Select + Create Headquarter from the above the list on the root page (path: /).
  2. You are redirected to the new headquarter form (path: /0).
  3. Enter a name and a city then select the Save button.
  4. Notice that when the new headquarter is saved, the user is alerted with Your data has been saved and the path is changed to something like /4?saved&timestamp=2024-10-26T10:34.234Z
  5. Select the OK button of the alert, the path returns to the new headquarter form (path: /0).

Please Note: I'm not very familiar with React, Fluent UI React v9, and React-Router, so I don't know if I'm doing something that is fundamentally wrong in my design. I'm trying to create a POC for converting a very large AngularJS web app that is a part of an n-tier application into React. I'm not allowed to change the existing API, and the end web app result has to be a very close match to the existing application. The application is used to track items in an internal manufacturing process and the UI does not fit the modern design pattern of one entity per form/page. The application is designed to work more like a traditional desktop application.

Expected Behavior

return redirect(`/${newId}?saved&timeStamp=${now.toISOString()}`);

This should redirect the user to the headquarter form and the page will remain there. Entering the form will cause the loader to fetch the newly created item data from the API and the new entry's data is displayed to the user.

Example URL flow:

  1. https://q5n5rr.csb.app
  2. https://q5n5rr.csb.app/0
  3. https://q5n5rr.csb.app/4?saved&timestamp=2024-10-26T10:34.234Z

Actual Behavior

return redirect(`/${newId}?saved&timeStamp=${now.toISOString()}`);

Redirects the user to the headquarter form and the browser address bar temporarily displays the path to the new entry, but then redirects the user back to the new headquarters form.

Example URL flow:

  1. https://q5n5rr.csb.app
  2. https://q5n5rr.csb.app/0
  3. https://q5n5rr.csb.app/4?saved&timestamp=2024-10-26T10:34.234Z
  4. https://q5n5rr.csb.app/0
@MChez MChez added the bug label Oct 16, 2024
@MChez
Copy link
Author

MChez commented Oct 17, 2024

It looks like the issue of the redirect continues down the path of the router even when using NavLink to navigate between child controls.

I have updated the sandbox code to include two pages below the pricing tab. (This was just thrown together so there is no styling.)

The router has been updated to this:

  const router = createBrowserRouter([
    {
      path: "/",
      element: <Root />,
      errorElement: <ErrorPage />,
      children: [
        {
          // This route has no path, which lets it participate in the UI layout without requiring new path segments in the URL.
          errorElement: <ErrorPage />,
          children: [
            {
              // Tells the router to match and render this route when the user is at the parent route's exact path.
              index: true,
              element: <HqList />,
              loader: getHqsLoader,
            },
            {
              path: ":hqId",
              element: <HqTabLayout />,
              loader: getHqLoader,
              children: [
                {
                  index: true,
                  element: <General />,
                  loader: getHqLoader,
                  action: actionHqSave,
                },
                {
                  path: "pricing",
                  element: <PricingLayout />,
                  children: [
                    {
                      index: true,
                      element: <HqPage1 />,
                    },
                    {
                      path: "page2",
                      element: <HqPage2 />,
                    },
                  ],
                },
              ],
            },
          ],
        },
      ],
    },
  ]);

PricingLayout contains an Outlet component and two NavLink components. The NavLink components allow the user to navigate between the Page1 component and the Page2 component. Page1 and Page2 raise an alert when they load (2 x alerts because of the developer mode). Selecting the PAGE 2 link on the pricing tab will display the page 2 loaded alerts then the page 1 loaded alerts and the Page1 component is displayed.

To reproduce:

  1. Select one of the existing headquarters from the list on the root page.
  2. Select the Pricing tab from the top of the page.
  3. Select the OK button to close the alert for page 1 loading (x2).
  4. Select the PAGE 2 link to navigate to the second child page.
  5. Select the OK button to close the alert for page 2 loading (x2).
  6. Select the OK button to close the alert for page 1 loading (x2).

@WalleMechson
Copy link

Can you try using useNavigate() hook?

 const navigate = useNavigate();
 navigate(`/${newId}?saved&timestamp=${now}`, { replace: true });

I believe I have read same issue on the post of StackOverflow with solution being using the useNavigate hook instead of redirect function.

@MChez
Copy link
Author

MChez commented Oct 20, 2024

Hi @WalleMechson, thanks for the response.

Sorry for the delay in getting back to you, but I've been in Dublin for a couple of days.

Unfortunately, using the useNavigate hook results in the same behaviour. Because it's a hook I modified the action to always return the response from the API. I then added code to the useEffect for fetcher.data in the general.tsx component to respond to responses that had a status of 200 and used the navigate there.

Even if useNavigate had worked as a workaround, there would still be the NavLink issue. I think this is a bug that needs investigation and a proper resolution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants