remix-run / react-router

Declarative routing for React
https://reactrouter.com
MIT License
53.16k stars 10.31k forks source link

[v6] Redirect after finishing authentication #7879

Closed quanphm closed 3 years ago

quanphm commented 3 years ago

Version

React-router v6

Test Case

https://codesandbox.io/s/react-router-v6-auth-x1onb

Problem

Hi folks,

I tried to mimic this Redirects (Auth) example of react-router@5 into react-router@6.

There is a problem after users finished the authentication. Instead of redirecting users base on the function navigate after logging in successfully.

let login = () => {
  auth.signin(() => {
    navigate(from, { replace: true });
  });
};

The page always redirects you to the default protected due to this line of codes.

<Route
  {...rest}
  element={!auth.user ? element : <Navigate to="/protected" replace />}
/>

I doubt that this happens since my auth state has changed in the <PublicRoute> component, so it re-rendered again and the navigate in login callback gets overwritten by another navigate in <Navigate> component.

Steps to reproduce

  1. Go to /test by clicking on Test-Auth link without authentication. You will be redirected to the Login page.
  2. After clicking the Login button -> will be Redirected to /protected

Expected Behavior

After logging in, you should be redirected to /test, which is the previous page you intended to go in from the beginning.

myNameIsDu commented 3 years ago
function PublicRoute({ element, ...rest }) {
  let auth = useAuth();
  console.log(rest.path, auth.user);
  return (
    <Route
      {...rest}
      element={!auth.user ? element : <Navigate to="/protected" replace />}
    />
  );
}

Because you added this logic to the Login component, It will override the jump test

quanphm commented 3 years ago

Hi @myNameIsDu,

Thanks for the answer but can you elaborate more?

Here is an example of the same thing but in react-route@5. Its behavior does not look the same as v6.

https://codesandbox.io/s/react-router-v52-redirect-89rxe?file=/example.js

This example was created base on the official example from react-router https://reactrouter.com/web/example/auth-workflow I just added the PublicRoute which is a reverse version of PrivateRoute.

quanphm commented 3 years ago

Hi @timdorr

Can you reopen this issue? Since I have another question related to this.

myNameIsDu commented 3 years ago

@mikunpham I think this is the reason for React. Check out these two examples: react-router5:

class Children extends React.Component {
    componentDidMount() {
        console.log('didmount');
    }

    render() {
        return <div>children</div>;
    }
}

function App() {
    const [state, setState] = React.useState(0);
    const onClick = () => {
        setTimeout(() => {
            setState(v => ++v);
            console.log('push');
        });
    };

    return (
        <>
            <button onClick={onClick}>test btn</button>
            {state > 0 ? <Children /> : null}
        </>
    );
}

react-router6:

function Children() {
    React.useEffect(() => {
        console.log('children');
    }, []);

    return <div>children</div>;
}

function App() {
    const [state, setState] = React.useState(0);
    const onClick = () => {
        setTimeout(() => {
            setState(v => ++v);
            console.log('push');
        });
    };

    return (
        <>
            <button onClick={onClick}>test btn</button>
            {state > 0 ? <Children /> : null}
        </>
    );
}

You can see that the execution order of the two codes is different

quanphm commented 3 years ago

Thank you @myNameIsDu

Do you think that the problem comes from changing <Redirect /> class component to <Navigate /> function component cause this issue? For now, I just setTimeout(() => navigate(URL), 0) as a temporary fix and still looking forward to an alternative solution for this case.