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 InstancenextOrObserver
: a callback triggered on authentication changeerror
: a callback triggered on errorcompleted
(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>
)
};