useNavigate() 只能在 <Router> 组件的上下文中使用。 (在不同的文件上)

问题描述 投票:0回答:1

我一直在做一个需要登录的项目。我一直在关注我在搜索信息时发现的this教程(这是本教程的第一种方式)。

但是,由于我想在单独的文件中创建项目,因此我无法直接按照教程进行操作。我有多个文件:

  • App.js
    创建应用程序的位置。
  • log-in.js
    登录页面所在位置
  • authoriser.js
    处理用户的身份验证(按下按钮后)
  • auth-server.js
    创建一个所有用户所在的数据库

我会展示每个文件的关键部分:

App.js

import React, { useEffect, useState } from 'react';
import { BrowserRouter as Router, Routes, Route, useNavigate } from 'react-router-dom';
import './App.css';


import Home from './pages/home';
import LogIn from './pages/log-in';
import Dashboard from './pages/dashboard';

import ProtectedRoute from './components/protected-route';
export default function App() {

  const navigate = useNavigate();

  const [username, setUsername] = useState('');
  const [loggedIn, setLoggedIn] = useState(false);

  useEffect(() => {
    const user = JSON.parse(localStorage.getItem('user'));

    if (!user || !user.token) {
      setLoggedIn(false);
      return;
    }

    fetch('http://localhost:3080/verify', {
      method: 'POST',
      headers: {
        'jwt-token': user.token
      },
    })
    .then((r) => r.json())
    .then((r) => {
      setLoggedIn('success' === r.message);
      setUsername(user.username || '');
    });
  }, []);

  return (
      <Router>
        <userContext.Provider value={username}>
          <setUserContext.Provider value={username}>
          <Routes>
            <Route path='/' element={<Home />} />
            <Route path='/login' element={<LogIn setLoggedIn={setLoggedIn} setUsername={setUsername} navigate={navigate} />} />
            <Route element={<ProtectedRoute user={username} />}>
              <Route path='/dashboard' element={<Dashboard loggedIn={loggedIn} setLoggedIn={setLoggedIn} />} />
            </Route>
          </Routes>
          </setUserContext.Provider>
        </userContext.Provider>
      </Router>
  );
}

log-in.js

import React, { useEffect, useState } from "react";
import LogInNavbar from "../components/navbar/login-navbar";
import Authoriser from "../components/authoriser";
import { useNavigate } from "react-router-dom";

export default function LogIn(props) {
    
    const [formData, setFormData] = useState({ username: "", password: "" });
    //const navigate = useNavigate();

    const onChange = (event) => {
        setFormData({...formData, [event.target.name]: event.target.value});
    };
    
    useEffect(()=>{
        document.body.style.overflow = "hidden";
    });

    function authoriser(){       
        console.log("Authoriser");
        Authoriser(formData, props);
        if (props.loggedIn) {
            localStorage.removeItem('user');
            props.setLoggedIn(false);
        } else {
            props.navigate('/login');
        }
    }
    
    return (
        <>
        <div className="Navbar">
            <LogInNavbar />
        </div>
        <div className="LogIn">
        <form className="LogInForm">
                <h3>Iniciar Sesión</h3>

                <label htmlFor="username">Nombre de Usuario</label>
                <input type="text" placeholder="Nombre de Usuario" name="username" value={formData.username} onChange={onChange} />

                <label htmlFor="password">Contraseña</label>
                <input type="password" placeholder="Contraseña" name="password" value={formData.password} onChange={onChange} />

                <button onClick={authoriser}>
                    Iniciar Sesión
                </button>
        </form>
        </div>
        </>
    );
}

authoriser.js

export default function Authoriser( formData, props ) {

    //const navigate = useNavigate();

    const username = formData.username;
    const password = formData.password;
    
    const user = {username: username, password: password};
    
    console.log(user);

    if (username === ''){
        window.alert("Introduzca su nombre de usuario");
        return;
    }

    if (password === ''){
        window.alert("Introduzca su contraseña");
        return;
    }
    
    checkAccountExists((accountExists) => {
        if (accountExists) proceedLoggingIn()
        else if (
            window.confirm(
                'No existe ninguna cuenta con este nombre de usuario: ' + username + '. ¿Quieres crear una nueva cuenta?'
            )
        ) {
            proceedLoggingIn();
        }
    });

    // Call the server API to check if the username ID already exists
    const checkAccountExists = (callback) => {
        fetch('http://localhost:3080/check-account', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ username })
        })
        .then((r) => r.json())
        .then((r) => {
            callback(r?.userExists);
        });
    }
   
    // Log in a user using username and password
    const proceedLoggingIn = () => {
        fetch('http://localhost:3080/check-account', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ username, password })
        })
        .then((r) => r.json())
        .then((r) => {
            if ('success' == r.message) {
                localStorage.setItem('user', JSON.stringify({ username, token: r.token }));
                props.setLoggedIn(true);
                props.setUsername(username);
                props.navigate('/dashboard');
            } else {
                window.alert('Wrong username or password');
            }
        })
    }
}

当我执行该代码时,它返回多个错误,全部显示

useNavigate() may be used only in the context of a <Router> component.

如果有人能帮助我解决问题,我将不胜感激。

我尝试过传递

navigate
作为使用它们的道具(我认为它和我收到
Invalid hook call
错误之前一样有效)。

我不知道为什么我不能在

authoriser.js
文件中使用导航,因为它会删除该错误。我的理论是,这是因为它是一个事件处理程序(因为它处理登录单击按钮)。

我还看到该组件必须位于 Router 和 Routes 上,但它被包裹在其上。

我曾经在页面之间移动

<Link>
,但在这种情况下我不知道如何实现这种情况,因为它不返回页面元素。也许有一种方法可以调用其他网络路径,但我不知道它们以及如何在我的代码中实现它们。

react-hooks react-router react-navigation react-router-dom
1个回答
0
投票

App
渲染
Router
以及路由上下文。将
Router
移至 ReactTree 上方。

在组件渲染中

App
:

import { BrowserRouter as Router } from 'react-router-dom';
import App from './components/App';

...

<Router>
  </App />
</Router>

应用程序

import React, { useEffect, useState } from 'react';
import { Routes, Route, useNavigate } from 'react-router-dom';
import './App.css';

import Home from './pages/home';
import LogIn from './pages/log-in';
import Dashboard from './pages/dashboard';

import ProtectedRoute from './components/protected-route';

export default function App() {
  const navigate = useNavigate();

  const [username, setUsername] = useState('');
  const [loggedIn, setLoggedIn] = useState(false);

  useEffect(() => {
    const user = JSON.parse(localStorage.getItem('user'));

    if (!user || !user.token) {
      setLoggedIn(false);
      return;
    }

    fetch('http://localhost:3080/verify', {
      method: 'POST',
      headers: {
        'jwt-token': user.token
      },
    })
    .then((r) => r.json())
    .then((r) => {
      setLoggedIn('success' === r.message);
      setUsername(user.username || '');
    });
  }, []);

  return (
    <userContext.Provider value={username}>
      <setUserContext.Provider value={setUsername}>
        <Routes>
          <Route path='/' element={<Home />} />
          <Route
            path='/login'
            element={(
              <LogIn
                setLoggedIn={setLoggedIn}
                setUsername={setUsername}
              />
            )}
          />
          <Route element={<ProtectedRoute user={username} />}>
            <Route
              path='/dashboard'
              element={(
                <Dashboard
                  loggedIn={loggedIn}
                  setLoggedIn={setLoggedIn}
                />
              )}
            />
          </Route>
        </Routes>
      </setUserContext.Provider>
    </userContext.Provider>
  );
}

有关上下文提供者和道具的附加说明。这些组件不应将

username
setUsername
作为 props 向下传递,而应使用提供的 React 上下文值,即使用
useContext
钩子。

© www.soinside.com 2019 - 2024. All rights reserved.