如何在 google 登录反应后重定向到另一个组件

How to redirect to another component after google signin in react

我想在用户通过他们的 gmail ID 登录后重定向到 <Chats /> 组件。我还希望 uiduserName 作为道具发送到组件,但我无法重定向到那个。我在 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

如果您不想在路由状态下发送数据,您可以尝试使用本地 uiduserName 状态并仍然使用 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>
  )
};