【问题标题】:React Router renders only after refreshReact Router 仅在刷新后呈现
【发布时间】:2021-07-04 09:52:02
【问题描述】:

我正在尝试使用 react redux 和路由器创建一个 Web 应用程序,但我遇到了一个奇怪的行为,路由器仅在刷新时呈现。 我的 App.js

import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Router, Switch, Route, Link } from "react-router-dom";
import { ModalProvider } from './modal/modalContext';
import "bootstrap/dist/css/bootstrap.min.css";
import "./App.css";
import Login from "./components/Login";
import Register from "./components/Register";
import Home from "./components/Home";
import Profile from "./components/Profile";
import BoardUser from "./components/BoardUser";
import BoardModerator from "./components/BoardModerator";
import BoardAdmin from "./components/BoardAdmin";
import DataService from "./services/data.service";
import { logout } from "./actions/auth";
import { clearMessage } from "./actions/message";
import { setDc } from "./actions/dc";
import { history } from "./helpers/history";
import simplidc_logo from "./images/dns-24px.svg"
import DetailedRackCard from './components/detailed_rack_card'

const App = () => {
  const [showModeratorBoard, setShowModeratorBoard] = useState(false);
  const [showAdminBoard, setShowAdminBoard] = useState(false);

  const { user: currentUser } = useSelector((state) => state.auth);

  const dispatch = useDispatch();
  useEffect(() => {
    history.listen((location) => {
      dispatch(clearMessage()); // clear message when changing location
    });
  }, [dispatch]);

  useEffect(() => {
    if (currentUser) {
      setShowModeratorBoard(currentUser.roles.includes("ROLE_MODERATOR"));
      setShowAdminBoard(currentUser.roles.includes("ROLE_ADMIN"));
    }
  }, [currentUser]);

  useEffect(() => {
    DataService.getDevices().then(
      (devicesResponse) =>{
        DataService.getRacks().then(
          (racksResponse) =>{
            dispatch(setDc(DataService.getDc(racksResponse,devicesResponse)));
          }
        )
      }
    )
}, [dispatch]);
  const logOut = () => {
    dispatch(logout());
  };

  return (
    <div>
    <ModalProvider>
    <Router history={history}>
      <div>
        <nav className="navbar navbar-expand navbar-dark bg-dark">
          <Link to={"/"} className="navbar-brand">
            SimpliDC
            <img
                        alt=""
                        src={simplidc_logo}
                        width="30"
                        height="30"
                    />
          </Link>
          <div className="navbar-nav mr-auto">
            <li className="nav-item">
              <Link to={"/home"} className="nav-link">
                Home
              </Link>
            </li>

            {showModeratorBoard && (
              <li className="nav-item">
                <Link to={"/mod"} className="nav-link">
                  Moderator Board
                </Link>
              </li>
            )}

            {showAdminBoard && (
              <li className="nav-item">
                <Link to={"/admin"} className="nav-link">
                  Admin Board
                </Link>
              </li>
            )}

            {currentUser && (
              <li className="nav-item">
                <Link to={"/user"} className="nav-link">
                  User
                </Link>
              </li>
            )}
          </div>

          {currentUser ? (
            <div className="navbar-nav ml-auto">
              <li className="nav-item">
                <Link to={"/profile"} className="nav-link">
                  {currentUser.username}
                </Link>
              </li>
              <li className="nav-item">
                <a href="/login" className="nav-link" onClick={logOut}>
                  LogOut
                </a>
              </li>
            </div>
          ) : (
            <div className="navbar-nav ml-auto">
              <li className="nav-item">
                <Link to={"/login"} className="nav-link">
                  Login
                </Link>
              </li>

              <li className="nav-item">
                <Link to={"/register"} className="nav-link">
                  Sign Up
                </Link>
              </li>
            </div>
          )}
        </nav>
        <input className="" type="text" placeholder="Type any vaule to search in the DC..."/>

        <div className="container mt-3">

          <Switch>
            <Route exact path={["/", "/home"]} component={Home} />
            <Route exact path="/login" component={Login} />
            <Route exact path="/register" component={Register} />
            <Route exact path="/profile" component={Profile} />
            <Route path="/user" component={BoardUser} />
            <Route path="/mod" component={BoardModerator} />
            <Route path="/admin" component={BoardAdmin} />
            <Route path="/rack/:id" component={DetailedRackCard}/>
          </Switch>

        </div>
      </div>
    </Router>
    </ModalProvider>
    </div>
  );
};

export default App;

我在查看路由器组件上的反应开发工具时发现了一些奇怪的事情。这是刷新后的状态位置:

这是我点击路线(登录)后的同一个对象:

我急于寻求帮助.... 是的,这是我整个应用程序中唯一的路由器。

谢谢!

【问题讨论】:

    标签: react-redux react-router react-hooks


    【解决方案1】:

    &lt;Router/&gt;state 来自您提供的 history 道具。您没有正确修改 history。坏突变将在另一个文件中的某处。似乎您正在调用 history.push() 并使用整个操作对象,而不仅仅是 action.location 属性。

    &lt;Router/&gt; 组件与自定义history 属性一起使用是一种高级设计模式,除非绝对必要,否则您通常希望避免这种模式。我认为这里没有必要,至少对于这个特定文件中的代码来说不是。

    我建议使用&lt;BrowserRouter/&gt;(或&lt;HashRouter/&gt;等代替。如果您这样做,您将需要一些其他方式来处理history.listen订阅(旁注:您想使用清理功能当组件卸载时停止监听)。

    您可以使用useEffect 挂钩来响应您从useLocation 挂钩访问的位置的变化。

    const location = useLocation();
    
    useEffect(() => {
      dispatch(clearMessage()); // clear message when changing location
    }, [location, dispatch]);
    

    如果您不想在第一次渲染时清除消息,您可以在调度之前添加一些额外的检查。您可以使用 usePrevious 自定义钩子或类似的东西。这里我使用useRef 来存储之前的位置。 ref 的初始值是来自useLocationlocation,因此在第一次渲染时它们将相等。

    const location = useLocation();
    
    const locRef = useRef(location);
    
    useEffect(() => {
      console.log(locRef.current, location);
      // will be equal on first render
      if (locRef.current !== location) {
        dispatch(clearMessage());
      }
      locRef.current = location;
    }, [location, dispatch]);
    

    如果需要,您可以修改它以仅查看 pathname

    【讨论】:

    • 谢谢!我切换到 BrowserRouter,现在它工作得很好!