首页 > 解决方案 > React - 即使刷新了如何保持在同一页面上?

问题描述

我正在使用 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="/"
          render={() => (
            <MainLayout>
              <Homepage />
            </MainLayout>
          )}
        />
        <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 是访客用户,它会将用户定向到登录页面。

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

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

export default withRouter(WithAuth);

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

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")
);

减速器:用户类型:

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,
});

用户减速器:

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;

根减速器:

import { combineReducers } from "redux";

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

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

store.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;

checkUserAdmin.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) 这就是显示的内容: 在此处输入图像描述

标签: javascriptreactjsfirebasereact-reduxreact-router

解决方案


我建议在 firestore 逻辑处理用户更改时向您的, initial 添加一个authPending状态,并设置/清除。userReducertrue

userReducer & 动作

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,
};

钩子和包装纸

对于这些,我建议将逻辑抽象为自定义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 中,并在加载应用程序时实例化(参数)对象时从 localStorage 重新填充您reduxstore状态您可以管理自己或查看类似redux-persist 的内容preloadedState


推荐阅读