如何在 google 登录反应后重定向到另一个组件
How to redirect to another component after google signin in react
我想在用户通过他们的 gmail ID 登录后重定向到 <Chats />
组件。我还希望 uid
和 userName
作为道具发送到组件,但我无法重定向到那个。我在 promise 里面试过 <Navigate />
,我也试过 window.location.replace('')
。 <Navigate />
似乎什么都不做,window.location.replace('')
重定向但是道具是空的,我不知道发生了什么,有人可以帮忙吗?
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
在 Firebase 中,有一个功能可以帮助您在身份验证更改时(即:使用电子邮件登录后、注册后、注销后...)执行一些代码,它是:
onAuthStateChanged(auth: Auth, nextOrObserver: NextOrObserver<User>, error?: ErrorFn, completed?: CompleteFn): Unsubscribe;
其中:
auth
:验证实例
nextOrObserver
: 身份验证更改时触发的回调
error
: 错误触发回调
completed
(可选):移除观察者时触发的回调
您可以在以下位置阅读更多信息:https://firebase.google.com/docs/reference/js/auth.md#onauthstatechanged
基本上,当使用这个函数时,你是在订阅认证观察者,而这个函数returns是一个取消订阅函数。因此,这可以完美地与 React 中的 useEffect
一起使用。看到您的问题后,您可以将这些行放入 App
组件中:
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
})
在查看您的代码时,我发现您将所有身份验证方法和状态都放在 App
组件中。我建议您创建一个包含这些代码行(以及上面的代码)的 React 上下文,因为它们应该是全局的。
可以把首页和聊天放在同一个url路径下,渲染出符合要求的页面
<Routes>
<Route path="/" element={uid || userName ? <Chats uid={uid} name={userName}/> :<Homepage signInWithGoogle={signInWithGoogle} signOutWithGoogle={signOutWithGoogle} />} />
</Routes>
或
<Routes>
{uid || userName ?
<Route path="/chats" element={<Chats uid={uid} name={userName}/>} />
:
<Route path="/" element={<Homepage signInWithGoogle={signInWithGoogle} signOutWithGoogle={signOutWithGoogle} />} />
}
</Routes>
问题
你不能 return 回调中的 JSX 并期望它被渲染到 DOM 中并做任何事情。
不要使用 window.location
方法,因为这会重新加载页面,从而重新加载您的应用程序,因此如果没有持久保存到 localStorage 或类似的,任何 React 状态都将丢失。
解决方案 1
使用 useNavigate
挂钩访问 navigate
函数并发出命令式重定向,并在路由状态中发送适当的数据。不过,为了让 App
组件使用 useNavigate
挂钩,您需要在 ReactTree 中将 Router
提升到它上面。
示例:
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 或呈现 App
的任何地方
import { BrowserRouter as Router } from 'react-router-dom';
...
<Router>
<App />
</Router>
聊天
使用 useLocation
挂钩访问传递的路由状态值。
const { state } = useLocation();
...
const { uid, userName } = state || {};
解决方案 2
如果您不想在路由状态下发送数据,您可以尝试使用本地 uid
和 userName
状态并仍然使用 navigate
函数来强制重定向。您仍然需要将 Router
提升到 ReactTree。
示例:
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>
)
};
我想在用户通过他们的 gmail ID 登录后重定向到 <Chats />
组件。我还希望 uid
和 userName
作为道具发送到组件,但我无法重定向到那个。我在 promise 里面试过 <Navigate />
,我也试过 window.location.replace('')
。 <Navigate />
似乎什么都不做,window.location.replace('')
重定向但是道具是空的,我不知道发生了什么,有人可以帮忙吗?
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
在 Firebase 中,有一个功能可以帮助您在身份验证更改时(即:使用电子邮件登录后、注册后、注销后...)执行一些代码,它是:
onAuthStateChanged(auth: Auth, nextOrObserver: NextOrObserver<User>, error?: ErrorFn, completed?: CompleteFn): Unsubscribe;
其中:
auth
:验证实例nextOrObserver
: 身份验证更改时触发的回调error
: 错误触发回调completed
(可选):移除观察者时触发的回调
您可以在以下位置阅读更多信息:https://firebase.google.com/docs/reference/js/auth.md#onauthstatechanged
基本上,当使用这个函数时,你是在订阅认证观察者,而这个函数returns是一个取消订阅函数。因此,这可以完美地与 React 中的 useEffect
一起使用。看到您的问题后,您可以将这些行放入 App
组件中:
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
})
在查看您的代码时,我发现您将所有身份验证方法和状态都放在 App
组件中。我建议您创建一个包含这些代码行(以及上面的代码)的 React 上下文,因为它们应该是全局的。
可以把首页和聊天放在同一个url路径下,渲染出符合要求的页面
<Routes>
<Route path="/" element={uid || userName ? <Chats uid={uid} name={userName}/> :<Homepage signInWithGoogle={signInWithGoogle} signOutWithGoogle={signOutWithGoogle} />} />
</Routes>
或
<Routes>
{uid || userName ?
<Route path="/chats" element={<Chats uid={uid} name={userName}/>} />
:
<Route path="/" element={<Homepage signInWithGoogle={signInWithGoogle} signOutWithGoogle={signOutWithGoogle} />} />
}
</Routes>
问题
你不能 return 回调中的 JSX 并期望它被渲染到 DOM 中并做任何事情。
不要使用 window.location
方法,因为这会重新加载页面,从而重新加载您的应用程序,因此如果没有持久保存到 localStorage 或类似的,任何 React 状态都将丢失。
解决方案 1
使用 useNavigate
挂钩访问 navigate
函数并发出命令式重定向,并在路由状态中发送适当的数据。不过,为了让 App
组件使用 useNavigate
挂钩,您需要在 ReactTree 中将 Router
提升到它上面。
示例:
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 或呈现 App
的任何地方
import { BrowserRouter as Router } from 'react-router-dom';
...
<Router>
<App />
</Router>
聊天
使用 useLocation
挂钩访问传递的路由状态值。
const { state } = useLocation();
...
const { uid, userName } = state || {};
解决方案 2
如果您不想在路由状态下发送数据,您可以尝试使用本地 uid
和 userName
状态并仍然使用 navigate
函数来强制重定向。您仍然需要将 Router
提升到 ReactTree。
示例:
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>
)
};