Home > Software design >  How to redirect to another component after google signin in react
How to redirect to another component after google signin in react

Time:05-18

I want to redirect to <Chats /> component after the user has signed in through their gmail ID. I also want the uid and userName to be send as props to the component, but I'm not able to redirect to that. I've tried <Navigate /> inside the promise, I've tried window.location.replace('') too. <Navigate /> seems to do nothing and window.location.replace('') redirects but the props are empty, I have no idea what is happening, can anyone help?

Here is the code:

import React, {useState} from 'react'
import { signInWithPopup, signOut } from 'firebase/auth'
import { auth, db, provider } from './firebase-config'
import { set, ref, onValue } from 'firebase/database'
import { BrowserRouter as Router, Routes, Route, Navigate} from 'react-router-dom'
import Chats from './components/Chats'
import Homepage from './components/Homepage'

const App = () => {

    const [uid, setUid] = useState('')
    const [userName, setUserName ] = useState('')
    const [redirect, setRedirect] = useState(false)

    const signInWithGoogle = () => {
        signInWithPopup(auth, provider)
            .then(result => {
                setUid(result.user.uid)
                setUserName(result.user.displayName)
                setRedirect(true)
            }).catch(err => console.log(err))
    }

    const signOutWithGoogle = () => {
      signOut(auth, provider)
      setUserName('')
      setRedirect(false)
    }

    redirect && (<Navigate to='/chats' />)

  return (
    <Router>
        <Routes>
            <Route path="/" element={<Homepage signInWithGoogle={signInWithGoogle} signOutWithGoogle={signOutWithGoogle} />} />
            <Route path="/chats" element={<Chats uid={uid} name={userName}/>} />
        </Routes>

    </Router>
  )
}

export default App

CodePudding user response:

In Firebase, there's one function that can help you to implement some code when the authentication change (i.e.: after logging in using an email, after signing up, after logging out, ...), which is:

onAuthStateChanged(auth: Auth, nextOrObserver: NextOrObserver<User>, error?: ErrorFn, completed?: CompleteFn): Unsubscribe;

Where:

  • auth: The Auth Instance
  • nextOrObserver: a callback triggered on authentication change
  • error: a callback triggered on error
  • completed (optional): a callback triggered when observer is removed

You can read more at: https://firebase.google.com/docs/reference/js/auth.md#onauthstatechanged

Basically, when using this function, you are subscribing to the authentication observer, and this function returns an unsubscribe function. Hence, this is can be perfectly used with useEffect in React. After seeing your problem, you can put these lines into the App component:

useEffect(() => {
  const unsubscribe = onAuthStateChanged(user => {
    // When no user is logged in and the path is '/chats' => redirect to login page
    if(user === null && window.location.href.includes('/chats')) {
      if(window.location.href.includes('/chats')) return; // This is important, if this line is not presented, the page will reload infinitely
      window.location.href = '/'; // Redirect to '/'
    }

    // After user is logged in => redirect to '/chats'
    if(user !== null && window.location.href.includes('/')) {
      if(window.location.href.includes('/')) return;
      window.location.href = '/chats'; // Redirect to '/chats'
    }
  });
  return unsubcribe; // This must also included
})

When seeing your code, I see that you put all authentication methods and states in App component. I recommend you to create a React state that contains those lines of code (and also the above code), since they should be global.

CodePudding user response:

you can put both homepage and chats under the same url path and render the page that meets the requirements

 <Routes>
     <Route path="/" element={uid || userName ? <Chats uid={uid} name={userName}/> :<Homepage signInWithGoogle={signInWithGoogle} signOutWithGoogle={signOutWithGoogle} />} />
 </Routes>

or

<Routes>
    {uid || userName ?
       <Route path="/chats" element={<Chats uid={uid} name={userName}/>} />
       :
       <Route path="/" element={<Homepage signInWithGoogle={signInWithGoogle} signOutWithGoogle={signOutWithGoogle} />} />
     }
</Routes>

CodePudding user response:

Issues

You can't return JSX from a callback and expect it to be rendered into the DOM and do anything.

Don't use the window.location method as this will reload the page, thus remounting your app, so any React state will be lost if it wasn't persisted to localStorage or similar.

Solution 1

Use the useNavigate hook to access the navigate function and issue an imperative redirect with the appropriate data sent along in route state. In order for the App component to use the useNavigate hook though, you will need to lift the Router above it in the ReactTree.

Example:

import { Routes, Route, useNavigate } from 'react-router-dom';

const App = () => {
  const navigate = useNavigate();

  const signInWithGoogle = () => {
    signInWithPopup(auth, provider)
      .then(result => {
        const { displayName, uid } = result.user.uid;
        navigate(
          "/chats",
          {
            replace: true,
            state: { uid, userName: displayName },
          }
        );
      })
      .catch(err => console.log(err));
  };

  const signOutWithGoogle = () => {
    signOut(auth, provider);
    setUserName('');
    setRedirect(false);
  };

  return (
    <Routes>
      <Route path="/" element={<Homepage signInWithGoogle={signInWithGoogle} signOutWithGoogle={signOutWithGoogle} />} />
      <Route path="/chats" element={<Chats />} />
    </Routes>
  );
};

index.js or wherever App is rendered

import { BrowserRouter as Router } from 'react-router-dom';

...

<Router>
  <App />
</Router>

Chat

Use the useLocation hook to access the passed route state values.

const { state } = useLocation();

...

const { uid, userName } = state || {};

Solution 2

If you don't want to send the data in route state you could try using the local uid and userName state and still use the navigate function to imperatively redirect. You'll still need to lift the Router up the ReactTree.

Example:

const App = () => {
  const navigate = useNavigate();
  const [uid, setUid] = useState('');
  const [userName, setUserName ] = useState('');

  const signInWithGoogle = () => {
    signInWithPopup(auth, provider)
      .then(result => {
        setUid(result.user.uid);
        setUserName(result.user.displayName);
        navigate("/chats", { replace: true });
      })
      .catch(err => console.log(err));
  };

  const signOutWithGoogle = () => {
    signOut(auth, provider);
    setUserName('');
    setRedirect(false);
  };

  return (
    <Routes>
      <Route path="/" element={<Homepage signInWithGoogle={signInWithGoogle} signOutWithGoogle={signOutWithGoogle} />} />
      <Route path="/chats" element={<Chats uid={uid} name={userName}/>} />
    </Routes>
  )
};
  • Related