javascript - React - 即使刷新了页面,如何保持在同一页面上?

标签 javascript reactjs firebase react-redux react-router

我正在使用 react-router 链接到不同的页面。一切正常,但是,一旦我刷新页面,它会进入登录页面一会儿,然后返回主页。更糟糕的是,如果我转到管理页面,刷新页面会将用户定向到登录页面,但是,用户仍然登录并且只显示登录页面。我还在使用 Firebase Firestore 和 firebase 身份验证。

应用程序.js

const App = (props) => {
  const { setCurrentUser, currentUser } = props;
  const admin = checkUserAdmin(currentUser);
  console.log(admin);

  useEffect(() => {
    const authListener = auth.onAuthStateChanged(async (userAuth) => {
      if (userAuth) {
        const userRef = await handleUserProfile(userAuth);
        userRef.onSnapshot((snapshot) => {
          setCurrentUser({
            id: snapshot.id,
            ...snapshot.data(),
          });
        });
      }

      setCurrentUser(userAuth);
    });

    return () => {
      authListener();
    };
  }, []);

  return (
    <div className="App">
      <Switch>
     
        <Route
          exact
          path="/login"
          render={() => (
            <MainLayout>
              <LoginPage />
            </MainLayout>
          )}
        />
        <Route
          exact
          path="/profile"
          render={() => (
            <WithAuth>
              <MainLayout>
                <ProfilePage />
              </MainLayout>
            </WithAuth>
          )}
        />
        <Route
          exact
          path="/admin"
          render={() => (
            <WithAdmin>
              <AdminHome />
            </WithAdmin>
          )}
        />
      
      </Switch>
    </div>
  );
};

const mapStateToProps = ({ user }) => ({
  currentUser: user.currentUser,
});

const mapDispatchToProps = (dispatch) => ({
  setCurrentUser: (user) => dispatch(setCurrentUser(user)),
});

export default connect(mapStateToProps, mapDispatchToProps)(App);

withAuth - 限制页面的用户。如果 currentUser 是 guest 用户,它将用户定向到登录页面。

import { useAuth } from "./../custom-hooks";
import { withRouter } from "react-router-dom";

const WithAuth = (props) => useAuth(props) && props.children;

export default withRouter(WithAuth);

useAuth - 限制页面的用户。如果 currentUser 是 guest 用户,它将用户定向到登录页面。

const mapState = ({ user }) => ({
  currentUser: user.currentUser,
});

const useAuth = (props) => {
  const { currentUser } = useSelector(mapState);

  useEffect(() => {
    if (!currentUser) {
      props.history.push("/login");
    }
  }, [currentUser]);

  return currentUser;
};

export default useAuth;

withAdmin - 只有管理员可以访问的页面

import { useAdmin } from "../../custom-hooks";

const WithAdmin = (props) => useAdmin(props) && props.children;

export default WithAdmin;

useAdmin - 页面只能由管理员访问。如果用户不是管理员,它会将用户定向到登录页面。

const mapState = ({ user }) => ({
  currentUser: user.currentUser,
});

const useAdmin = (props) => {
  const { currentUser } = useSelector(mapState);
  const history = useHistory();

  useEffect(() => {
    if (!checkUserAdmin(currentUser)) {
      history.push("/login");
    }
  }, [currentUser]);

  return currentUser;
};

export default useAdmin;

下面是我的index.js

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
);

reducer : 用户类型:

const userTypes = {
  SET_CURRENT_USER: "SET_CURRENT_USER",
};

export default userTypes;

用户操作:

import userTypes from "./user.types";

export const setCurrentUser = (user) => ({
  type: userTypes.SET_CURRENT_USER,
  payload: user,
});

用户 reducer :

import userTypes from "./user.types";

const INITIAL_STATE = {
  currentUser: null,
};

const userReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case userTypes.SET_CURRENT_USER:
      return {
        ...state,
        currentUser: action.payload,
      };
    default:
      return state;
  }
};

export default userReducer;

根 reducer :

import { combineReducers } from "redux";

import userReducer from "./user/user.reducer";

export default combineReducers({
  user: userReducer,
});

商店.js

import { createStore, applyMiddleware } from "redux";
import logger from "redux-logger";

import rootReducer from "./rootReducer";

export const middlewares = [logger];

export const store = createStore(rootReducer, applyMiddleware(...middlewares));

export default store;

检查用户管理.js

export const checkUserAdmin = (currentUser) => {
  if (!currentUser || !Array.isArray(currentUser.roles)) return false;
  const { roles } = currentUser;
  if (roles.includes("admin")) return true;

  return false;
};

在 App.js 中,我使用 console.log(currentUser) 显示以下内容: enter image description here

最佳答案

我建议向您的 userReducer 添加一个 authPending 状态,最初是 true,并且在 firestore 逻辑处理用户更改时设置/清除.

userReducer 和 Action

const userTypes = {
  SET_AUTH_PENDING: "SET_AUTH_PENDING",
  SET_CURRENT_USER: "SET_CURRENT_USER",
};

const setAuthPending = pending => ({
  type: userTypes.SET_AUTH_PENDING,
  payload: pending,
});

const INITIAL_STATE = {
  authPending: true,
  currentUser: null,
};

const userReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case userTypes.SET_CURRENT_USER:
      return {
        ...state,
        authPending: false
        currentUser: action.payload,
      };

    case userTypes.SET_AUTH_PENDING:
      return {
        ...state,
        authPending: action.payload,
      };

    default:
      return state;
  }
};

应用程序.js

const App = (props) => {
  const {
    setAuthPending, // <-- access action
    setCurrentUser,
    currentUser
  } = props;

  const admin = checkUserAdmin(currentUser);
  console.log(admin);

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged(async (userAuth) => {
      setAuthPending(true); // <-- start auth pending
      if (userAuth) {
        const userRef = await handleUserProfile(userAuth);
        userRef.onSnapshot((snapshot) => {
          setCurrentUser({ // <-- will clear auth pending
            id: snapshot.id,
            ...snapshot.data(),
          });
        });
      } else { 
        setCurrentUser(null); // <-- clear user data and pending
      }
    });

    return () => {
      unsubscribe();
    };
  }, []);

  return (
    <div className="App">
      <Switch>
        ...
      </Switch>
    </div>
  );
};

const mapStateToProps = ({ user }) => ({
  currentUser: user.currentUser,
});

const mapDispatchToProps = {
  setAuthPending, // <-- wrap action creator in call to dispatch
  setCurrentUser,
};

钩子(Hook)和包装器

对于这些,我建议将逻辑抽象到自定义 Route 组件中。

const AuthRoute = props => {
  const { authPending, currentUser } = useSelector(state => state.user);

  if (authPending) {
    return "Loading..."; // or maybe a loading spinner
  };

  return currentUser ? (
    <Route {...props} />
  ) : (
    <Redirect to="/login" />
  );
};

const AdminRoute = props => {
  const { authPending, currentUser } = useSelector(state => state.user);

  if (authPending) {
    return "Loading..."; // or maybe a loading spinner
  };

  return checkUserAdmin(currentUser) ? (
    <Route {...props} />
  ) : (
    <Redirect to="/login" />
  );
};

然后路线变成

<Switch>
  <Route
    exact
    path="/"
    render={() => (
      <MainLayout>
        <Homepage />
      </MainLayout>
    )}
  />
  <Route
    exact
    path="/login"
    render={() => (
      <MainLayout>
        <LoginPage />
      </MainLayout>
    )}
  />
  <AuthRoute
    exact
    path="/profile"
    render={() => (
      <MainLayout>
        <ProfilePage />
      </MainLayout>
    )}
  />
  <AdminRoute
    exact
    path="/admin"
    component={AdminHome}
  />
</Switch>

在此之后,您可能需要研究将 redux 状态保存到 localStorage 中,并在实例化 store 时从 localStorage 重新填充 redux 状态(preloadedState parameter) 对象在您的应用加载时。您可以自己管理或查看类似 redux-persist 的内容.

关于javascript - React - 即使刷新了页面,如何保持在同一页面上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68842442/

相关文章:

基于 css 的页面中的 Javascript 随机图像生成器

javascript - 使用 Prototype/Scriptaculous 预加载图像

javascript - 使用 refs 定位动态呈现的 ListView 组件

javascript - 指定组件所需的 props

android - 使用 Firebase 通过电子邮件发送用户密码

javascript - 获取元素和视口(viewport)右侧之间的距离以在滚动时更改 div 的宽度

javascript - jQuery .change() 对带有 1 个选项的选择没有反应

javascript - onClick 没有响应,即没有运行指定的函数

node.js - 如何从 firestore 数据库发送循环遍历数组的电子邮件

ios - Object = nil,但代码仍然在 if 语句中执行