Redux Store的状态在更改路线时会丢失/重置

发布于 2025-01-22 16:10:57 字数 7008 浏览 0 评论 0 原文

我有一个简单的应用程序,到目前为止,它只有一个主登录组件,还有一个带有NAVBAR的主页,一旦登录到该应用程序,就应该将用户路由到用户。用户体验应如下:用户从蓝色下拉菜单中选择名称,一旦选择了名称,他们就会单击绿色登录按钮,该按钮从loggedinslice.js派遣setLoggedInuser,并将所选用户设置为授权对象,原因是某种原因,我的状态是我的状态当我将登录按钮包装在路由器链接标签中时,从我的redux商店中丢失了,该标签通往主页,但是当该登录按钮不链接到任何地方时,它不会丢失。我想了解为什么一旦从UserLogin.js组件导航到main.js组件,我就不会保留我的Redux Authuser状态。

组件:

app.js

import logo from "./logo.svg";
import "./App.css";
import TestComponent from "./components/TestComponent";
import Main from "./components/Main";
import { Routes, Route, Link } from "react-router-dom";
import { useState } from "react";
import { Card, Button, Accordion, Dropdown } from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
import UserLogin from "./components/UserLogin";
import Nav from "./components/Nav";

function App() {
  return (
    <div className="App">
      <Routes>
        <Route path="home" element={<Main />} />
        <Route path="/" element={<UserLogin />} />
        <Route path="testcomponent" element={<TestComponent />} />
      </Routes>
    </div>
  );
}

export default App;

userLogin.js

import React from "react";
import { Routes, Route, Link, useNavigate } from "react-router-dom";
import { Redirect } from "react-router-dom";
import { useState, useEffect } from "react";
import { Card, Button, Accordion, Dropdown } from "react-bootstrap";
import { useSelector, useDispatch } from "react-redux";
import "bootstrap/dist/css/bootstrap.min.css";
import { setLoggedInUser } from "../features/loggedInSlice";

export default function UserLogin() {
  let navigate = useNavigate();
  const users = useSelector((state) => state.users);
  const state = useSelector((state) => state);
  const dispatch = useDispatch();
  const [user, setUser] = useState("Select A User");
  const [authorizedUser, setauthorizedUser] = useState({});
  const handleSelect = (e) => {
    setUser(e.target.text);
  };

  function authorizeUser() {
    setauthorizedUser(
      users.filter((userName) => userName.firstName === user)[0]
    );
  }

  useEffect(() => {
    dispatch(setLoggedInUser(authorizedUser));
  }, [authorizedUser]);

  function authorizeLogin() {
    console.log(state);
  }

  console.log(state);

  return (
    <div>
      <div
        style={{
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
        }}
      >
        <Card style={{ width: "50%" }}>
          <Card.Header>Would you Rather?</Card.Header>
          <Card.Body>
            <Card.Title>Special title treatment</Card.Title>
            <Card.Text>
              With supporting text below as a natural lead-in to additional
              content.
            </Card.Text>
          </Card.Body>
          <Dropdown className="d-inline mx-2" onClick={handleSelect}>
            <Dropdown.Toggle
              id="dropdown-autoclose-true"
              style={{ width: "60%" }}
            >
              {user}
            </Dropdown.Toggle>

            <Dropdown.Menu style={{ width: "60%" }}>
              {users.map((user, index) => (
                <Dropdown.Item href="#" key={index}>
                  {user.firstName}
                </Dropdown.Item>
              ))}
            </Dropdown.Menu>
          </Dropdown>
          <div>
<Link to="home">
            <Button
              variant="success"
              style={{ width: "20%", marginTop: "3%", marginBottom: "1%" }}
              onClick={authorizeUser}
            >
              Login
            </Button>
</Link>
          </div>
        </Card>

        <button onClick={authorizeLogin}>click</button>
      </div>
    </div>
  );
}

main.js

import React from "react";
import NavBar from "./Nav";
import { Routes, Route, Link } from "react-router-dom";

export default function Main() {
  return (
    <div>
      <NavBar />
    </div>
  );
}

nav.js

import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { Card, Button, Accordion, Dropdown, Nav } from "react-bootstrap";

export default function NavBar() {
  const state = useSelector((state) => state);

  console.log(state);

  const loggedInFirstName = state.loggedIn.authUser.firstName;

  return (
    <div style={{ display: "flex" }}>
      <Nav justify variant="tabs" defaultActiveKey="/home">
        <Nav.Item>
          <Nav.Link>Home</Nav.Link>
        </Nav.Item>
        <Nav.Item>
          <Nav.Link>New Poll </Nav.Link>
        </Nav.Item>
        <Nav.Item>
          <Nav.Link>Leaderboard</Nav.Link>
        </Nav.Item>
      </Nav>
      <h5 style={{ paddingLeft: "15px", paddingTop: "15px" }}>Welcome</h5>
    </div>
  );
}

redux Store和slices:

store.js

import { configureStore } from "@reduxjs/toolkit";
import usersReducer from "../features/usersSlice";
import loggedIn from "../features/loggedInSlice";

export const store = configureStore({
  reducer: {
    users: usersReducer,
    loggedIn: loggedIn,
  },
});

loggedinslice.js

import React from "react";

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  authUser: [],
};

export const loggedIn = createSlice({
  name: "loggedIn",
  initialState,
  reducers: {
    setLoggedInUser: (state, action) => {
      console.log(action.payload);
      state.authUser = action.payload;
    },
  },
});

export default loggedIn.reducer;
export const { setLoggedInUser } = loggedIn.actions;

userslice.js

import { createSlice } from "@reduxjs/toolkit";

const initialState = [
  {
    firstName: "matt",
    age: 30,
    total: 0,
  },

  {
    firstName: "mike",
    age: 25,
    total: 0,
  },
  {
    firstName: "steve",
    age: 22,
    total: 0,
  },
];

export const usersReducer = createSlice({
  name: "UsersSlice",
  initialState,
});

export default usersReducer.reducer;

要总结我需要理解为什么仅在登录按钮不添加我的redux商店中 另一个组件代码的路由

Authuser代码的

         <Button
              variant="success"
              style={{ width: "20%", marginTop: "3%", marginBottom: "1%" }}
              onClick={authorizeUser}
            >
              Login
            </Button>

添加不添加authuser的

<Link to="home">
            <Button
              variant="success"
              style={{ width: "20%", marginTop: "3%", marginBottom: "1%" }}
              onClick={authorizeUser}
            >
              Login
            </Button>
</Link>

I have a simple app that so far only has a main login component, and a home page with a navbar which the user is supposed to be routed to once they login to the app. The user experience should be as follows: user selects name from blue dropdown menu, once that name is selected, they click the green login button which dispatches setLoggedInUser from loggedInSlice.js and sets the selected user as the authorize object For some reason , my state from my redux store is lost when I wrap my login button in a router link tag which leads to the home page , but it is not lost when that login button doesnt link anywhere. I want to understand why I am not retaining my redux authUser state once I navigate from the userLogin.js component to the Main.js Component.

Components:

App.js

import logo from "./logo.svg";
import "./App.css";
import TestComponent from "./components/TestComponent";
import Main from "./components/Main";
import { Routes, Route, Link } from "react-router-dom";
import { useState } from "react";
import { Card, Button, Accordion, Dropdown } from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
import UserLogin from "./components/UserLogin";
import Nav from "./components/Nav";

function App() {
  return (
    <div className="App">
      <Routes>
        <Route path="home" element={<Main />} />
        <Route path="/" element={<UserLogin />} />
        <Route path="testcomponent" element={<TestComponent />} />
      </Routes>
    </div>
  );
}

export default App;

UserLogin.js

import React from "react";
import { Routes, Route, Link, useNavigate } from "react-router-dom";
import { Redirect } from "react-router-dom";
import { useState, useEffect } from "react";
import { Card, Button, Accordion, Dropdown } from "react-bootstrap";
import { useSelector, useDispatch } from "react-redux";
import "bootstrap/dist/css/bootstrap.min.css";
import { setLoggedInUser } from "../features/loggedInSlice";

export default function UserLogin() {
  let navigate = useNavigate();
  const users = useSelector((state) => state.users);
  const state = useSelector((state) => state);
  const dispatch = useDispatch();
  const [user, setUser] = useState("Select A User");
  const [authorizedUser, setauthorizedUser] = useState({});
  const handleSelect = (e) => {
    setUser(e.target.text);
  };

  function authorizeUser() {
    setauthorizedUser(
      users.filter((userName) => userName.firstName === user)[0]
    );
  }

  useEffect(() => {
    dispatch(setLoggedInUser(authorizedUser));
  }, [authorizedUser]);

  function authorizeLogin() {
    console.log(state);
  }

  console.log(state);

  return (
    <div>
      <div
        style={{
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
        }}
      >
        <Card style={{ width: "50%" }}>
          <Card.Header>Would you Rather?</Card.Header>
          <Card.Body>
            <Card.Title>Special title treatment</Card.Title>
            <Card.Text>
              With supporting text below as a natural lead-in to additional
              content.
            </Card.Text>
          </Card.Body>
          <Dropdown className="d-inline mx-2" onClick={handleSelect}>
            <Dropdown.Toggle
              id="dropdown-autoclose-true"
              style={{ width: "60%" }}
            >
              {user}
            </Dropdown.Toggle>

            <Dropdown.Menu style={{ width: "60%" }}>
              {users.map((user, index) => (
                <Dropdown.Item href="#" key={index}>
                  {user.firstName}
                </Dropdown.Item>
              ))}
            </Dropdown.Menu>
          </Dropdown>
          <div>
<Link to="home">
            <Button
              variant="success"
              style={{ width: "20%", marginTop: "3%", marginBottom: "1%" }}
              onClick={authorizeUser}
            >
              Login
            </Button>
</Link>
          </div>
        </Card>

        <button onClick={authorizeLogin}>click</button>
      </div>
    </div>
  );
}

Main.js

import React from "react";
import NavBar from "./Nav";
import { Routes, Route, Link } from "react-router-dom";

export default function Main() {
  return (
    <div>
      <NavBar />
    </div>
  );
}

Nav.js

import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { Card, Button, Accordion, Dropdown, Nav } from "react-bootstrap";

export default function NavBar() {
  const state = useSelector((state) => state);

  console.log(state);

  const loggedInFirstName = state.loggedIn.authUser.firstName;

  return (
    <div style={{ display: "flex" }}>
      <Nav justify variant="tabs" defaultActiveKey="/home">
        <Nav.Item>
          <Nav.Link>Home</Nav.Link>
        </Nav.Item>
        <Nav.Item>
          <Nav.Link>New Poll </Nav.Link>
        </Nav.Item>
        <Nav.Item>
          <Nav.Link>Leaderboard</Nav.Link>
        </Nav.Item>
      </Nav>
      <h5 style={{ paddingLeft: "15px", paddingTop: "15px" }}>Welcome</h5>
    </div>
  );
}

Redux Store and Slices:

store.js

import { configureStore } from "@reduxjs/toolkit";
import usersReducer from "../features/usersSlice";
import loggedIn from "../features/loggedInSlice";

export const store = configureStore({
  reducer: {
    users: usersReducer,
    loggedIn: loggedIn,
  },
});

loggedInSlice.js

import React from "react";

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  authUser: [],
};

export const loggedIn = createSlice({
  name: "loggedIn",
  initialState,
  reducers: {
    setLoggedInUser: (state, action) => {
      console.log(action.payload);
      state.authUser = action.payload;
    },
  },
});

export default loggedIn.reducer;
export const { setLoggedInUser } = loggedIn.actions;

usersSlice.js

import { createSlice } from "@reduxjs/toolkit";

const initialState = [
  {
    firstName: "matt",
    age: 30,
    total: 0,
  },

  {
    firstName: "mike",
    age: 25,
    total: 0,
  },
  {
    firstName: "steve",
    age: 22,
    total: 0,
  },
];

export const usersReducer = createSlice({
  name: "UsersSlice",
  initialState,
});

export default usersReducer.reducer;

To summarize I need to understand why my authUser is added to my redux store only when the login button does not route to another component

code that adds the authUser

         <Button
              variant="success"
              style={{ width: "20%", marginTop: "3%", marginBottom: "1%" }}
              onClick={authorizeUser}
            >
              Login
            </Button>

code that does not add the authUser

<Link to="home">
            <Button
              variant="success"
              style={{ width: "20%", marginTop: "3%", marginBottom: "1%" }}
              onClick={authorizeUser}
            >
              Login
            </Button>
</Link>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

浮世清欢 2025-01-29 16:10:57

问题

问题在于,重点反应状态更新是异步处理的。将登录按钮用链接包裹登录按钮时,导航操作到“/home” 在处理列为状态更新之前就发生了。 React认为, UserLogin 组件不再安装,并且忽略了状态更新( ssilesly )。

解决方案

我建议将“登录”逻辑移动到异步动作中。这将向 userLogin 组件返回可以等待和成功身份验证后的组件,将导航操作发行到“/home” 路径。

示例:

loggedinslice:

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";

const initialState = {
  authUser: []
};

const loginUser = createAsyncThunk(
  "loggedIn/loginHandler",
  async (user, { dispatch }) => {
    ... asynchronous login logic ...

    // if successfully logged in
    dispatch(loggedIn.actions.setLoggedInUser(user));
    return ... response object/success message/etc...;
  }
);

export const loggedIn = createSlice({
  name: "loggedIn",
  initialState,
  reducers: {
    setLoggedInUser: (state, action) => {
      state.authUser = action.payload;
    }
  }
});

export default loggedIn.reducer;
export const { setLoggedInUser } = loggedIn.actions;
export { loginUser };

UserLogin

直接在按钮的 onclick 处理程序中调度登录操作,等待已解决的承诺,然后导航。

...
import { loginUser } from "../features/loggedInSlice";

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

  const [user, setUser] = useState("Select A User");
  const users = useSelector((state) => state.users);

  const state = useSelector((state) => state);
  
  const dispatch = useDispatch();
  
  ...

  function authorizeUser() {
    const selectedUser = users.find((userName) => userName.firstName === user);

    dispatch(loginUser(selectedUser))
      .unwrap()
      .then((response) => {
        console.log({ response });
        navigate("/home");
      });
  }

  ...

  return (
    <div>
      <div ...>
        <Card ...>
          ...
          <div>
            <Button
              ...
              onClick={authorizeUser}
            >
              Login
            </Button>
          </div>
        </Card>
        ...
      </div>
    </div>
  );
}

Issue

The issue here is that enqueued React state updates are asynchronously processed. The navigation action to "/home" when wrapping the login button with a Link occurs well before the enqueued state update is processed. React sees that the UserLogin component is no longer mounted and ignores (silently) the state update.

Solution

I suggest moving the "login" logic into an asynchronous action. This will return a Promise to the UserLogin component that can be waited on and upon successful authentication issue the navigation action to the "/home" path.

Example:

loggedInSlice:

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";

const initialState = {
  authUser: []
};

const loginUser = createAsyncThunk(
  "loggedIn/loginHandler",
  async (user, { dispatch }) => {
    ... asynchronous login logic ...

    // if successfully logged in
    dispatch(loggedIn.actions.setLoggedInUser(user));
    return ... response object/success message/etc...;
  }
);

export const loggedIn = createSlice({
  name: "loggedIn",
  initialState,
  reducers: {
    setLoggedInUser: (state, action) => {
      state.authUser = action.payload;
    }
  }
});

export default loggedIn.reducer;
export const { setLoggedInUser } = loggedIn.actions;
export { loginUser };

UserLogin

Dispatch the login action directly in the button's onClick handler, wait for resolved Promise, then navigate.

...
import { loginUser } from "../features/loggedInSlice";

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

  const [user, setUser] = useState("Select A User");
  const users = useSelector((state) => state.users);

  const state = useSelector((state) => state);
  
  const dispatch = useDispatch();
  
  ...

  function authorizeUser() {
    const selectedUser = users.find((userName) => userName.firstName === user);

    dispatch(loginUser(selectedUser))
      .unwrap()
      .then((response) => {
        console.log({ response });
        navigate("/home");
      });
  }

  ...

  return (
    <div>
      <div ...>
        <Card ...>
          ...
          <div>
            <Button
              ...
              onClick={authorizeUser}
            >
              Login
            </Button>
          </div>
        </Card>
        ...
      </div>
    </div>
  );
}

Edit state-from-redux-store-gets-lost-reset-when-changing-route

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文