我刷新任何页面时总是被重定向到主页。

huangapple go评论62阅读模式
英文:

I always get redirected to home page when refresh any page

问题

我在类似情况的主题中进行了探讨,但仍然没有找到解决方案。

当我在 app.js 中实施了检查令牌函数以生成代码,以便未经身份验证的用户重定向到登录页面,而经过身份验证的用户跳转到主页时,我的单页面应用程序开始在刷新/F5时从任何页面重定向到主页。

以下是我的 authContext.js

export const AuthContext = createContext({
  token: null,
  userId: null,
  isLoggedIn: false,
  login: (userId, token) => {},
  logout: () => {},
});

const AuthContextProvider = (props) => {
  const [userId, setUserId] = useState();
  const [token, setToken] = useState();

  const loginUserHandler = useCallback((userId, token) => {
    setUserId(userId);
    setToken(token);
    localStorage.setItem("userData", JSON.stringify({ userId, token }));
  }, []);

  const logoutUserHandler = useCallback(() => {
    setUserId(null);
    setToken(null);
    localStorage.removeItem("userData");
  }, []);

  const initialValues = {
    userId: userId,
    token: token,
    isLoggedIn: !!token,
    login: loginUserHandler,
    logout: logoutUserHandler,
  };

  return <AuthContext.Provider value={initialValues}>{props.children}</AuthContext.Provider>;
};

export default AuthContextProvider;

app.js 代码:

function App() {
  const { token, login } = useContext(AuthContext);
  let routes;

  useEffect(() => {
    const storedData = JSON.parse(localStorage.getItem("userData"));
    if (storedData && storedData.token) {
      login(storedData.userId, storedData.token);
    }
  }, [login]);

  if (token) {
    routes = (
      <Layout>
        <Switch>
          <Route path="/home" exact>
            <Home />
          </Route>
          <Route path="/leads-manager" exact>
            <Leads />
          </Route>
          <Route path="/phone-manager" exact>
            <Phone />
          </Route>
          <Route path="/new-notifications" exact>
            <Notification />
          </Route>
          <Redirect to="/home" exact />
        </Switch>
      </Layout>
    );
  } else {
    routes = (
      <Switch>
        <Route path="/sign-in" exact>
          <Login />
        </Route>
        <Redirect to="/sign-in" />
      </Switch>
    );
  }

  return <BrowserRouter>{routes}</BrowserRouter>;
}

export default App;

希望这能帮助你解决问题。

英文:

I traveled through topics similar situation to mine, but I still not found the solution to my case.

When I implemented checking token function on app.js to generate code for Unauthenticated User redirect to Signin page and Authenticated User jump to Hompage.

However, after this part my SPA started getting redirect to home page from any page when refreshing/F5.

Below is my authContext.js:

export const AuthContext = createContext({
token: null,
userId: null,
isLoggedIn: false,
login: (userId, token) =&gt; {},
logout: () =&gt; {},
});
const AuthContextProvider = (props) =&gt; {
const [userId, setUserId] = useState();
const [token, setToken] = useState();
const loginUserHandler = useCallback((userId, token) =&gt; {
setUserId(userId);
setToken(token);
localStorage.setItem(&quot;userData&quot;, JSON.stringify({ userId, token }));
}, []);
const logoutUserHandler = useCallback(() =&gt; {
setUserId(null);
setToken(null);
localStorage.removeItem(&quot;userData&quot;);
}, []);
const initialValues = {
userId: userId,
token: token,
isLoggedIn: !!token,
login: loginUserHandler,
logout: logoutUserHandler,
};
return &lt;AuthContext.Provider value={initialValues}&gt;{props.children}&lt;/AuthContext.Provider&gt;;
};
export default AuthContextProvider;

And app.js code

function App() {
const { token, login } = useContext(AuthContext);
let routes;
useEffect(() =&gt; {
const storedData = JSON.parse(localStorage.getItem(&quot;userData&quot;));
if (storedData &amp;&amp; storedData.token) {
login(storedData.userId, storedData.token);
}
}, 
此处为隐藏的内容
注册登录后,方可查看
); if (token) { routes = ( &lt;Layout&gt; &lt;Switch&gt; &lt;Route path=&quot;/home&quot; exact&gt; &lt;Home /&gt; &lt;/Route&gt; &lt;Route path=&quot;/leads-manager&quot; exact&gt; &lt;Leads /&gt; &lt;/Route&gt; &lt;Route path=&quot;/phone-manager&quot; exact&gt; &lt;Phone /&gt; &lt;/Route&gt; &lt;Route path=&quot;/new-notifications&quot; exact&gt; &lt;Notification /&gt; &lt;/Route&gt; &lt;Redirect to=&quot;/home&quot; exact /&gt; &lt;/Switch&gt; &lt;/Layout&gt; ); } else { routes = ( &lt;Switch&gt; &lt;Route path=&quot;/sign-in&quot; exact&gt; &lt;Login /&gt; &lt;/Route&gt; &lt;Redirect to=&quot;/sign-in&quot; /&gt; &lt;/Switch&gt; ); } return &lt;BrowserRouter&gt;{routes}&lt;/BrowserRouter&gt;; } export default App;

答案1

得分: 1

问题在于在AuthContextProvider组件挂载时,您没有从本地存储初始化token状态。当重新加载页面时,由于React状态仅存在于内存中,所有React状态都将丢失。token是假值,因此App组件会呈现“未经身份验证”的路由/重定向。我认为发生的情况是token是假值,用户被重定向到"/sign-in",同时App中的useEffect挂钩从本地存储中提取并验证用户,然后更新userIdtoken状态。现在token被定义,因此会呈现“已验证”的路由,由于URL路径仍然是"/sign-in",将呈现Redirect"/home"

使用useEffect挂钩来(A)将状态更改持久保存到本地存储,并(B)初始化userIdtoken状态。

const AuthContextProvider = ({ children }) => {
  const [userId, setUserId] = useState();
  const [token, setToken] = useState();

  // 用于初始化本地状态和重新验证的效果
  useEffect(() => {
    const { userId, token } = JSON.parse(localStorage.getItem("userData")) || {};
    if (userId) setUserId(userId);
    if (token) setToken(token);

    if (userId && token) {
      login(userId, token);
    }
  }, []);

  useEffect(() => {
    if (token || userId) {
      localStorage.setItem("userData", JSON.stringify({ userId, token }));
    } else {
      localStorage.removeItem("userData");
    }
  }, [token, userId]);

  const loginUserHandler = useCallback((userId, token) => {
    setUserId(userId);
    setToken(token);
  }, []);

  const logoutUserHandler = useCallback(() => {
    setUserId(null);
    setToken(null);
  }, []);

  const initialValues = {
    userId: userId,
    token: token,
    isLoggedIn: !!token,
    login: loginUserHandler,
    logout: logoutUserHandler,
  };

  return (
    <AuthContext.Provider value={initialValues}>
      {children}
    </AuthContext.Provider>
  );
};

更新App组件,以明确检查在组件挂载时token是否未定义,以便有条件地提前返回,以便不应用“已验证”或“未验证”的路由/重定向逻辑。

function App() {
  const { token } = useContext(AuthContext);

  if (token === undefined) {
    return null; // 或加载指示器/旋转等
  }

  return (
    <BrowserRouter>
      {token
        ? (
          <Layout>
            <Switch>
              <Route path="/home" component={Home} />
              <Route path="/leads-manager" component={Leads} />
              <Route path="/phone-manager" component={Phone} />
              <Route path="/new-notifications" component={Notification} />
              <Redirect to="/home" />
            </Switch>
          </Layout>
        )
        : (
          <Switch>
            <Route path="/sign-in" component={Login} />
            <Redirect to="/sign-in" />
          </Switch>
        )
      }
    </BrowserRouter>
  );
}

export default App;
英文:

The issue is that you don't initialize the token state from localStorage in the AuthContextProvider component when the component mounts. When you reload the page all React state will be lost since it exists only in memory. token is falsey and the App component renders the "unauthenticated" route/redirect. I believe what is happening is the token is falsey and the user is bounced to &quot;/sign-in&quot; and at the same time the useEffect hook in App pulls from local storage and authenticates the user, which updates the userId and token states. token is defined now and the "authenticated" routes render, and because the URL path is still &quot;/sign-in&quot; the Redirect to &quot;/home&quot; is rendered.

Use useEffect hooks to (A) persist state changes to localStorage and (B) initialize the userId and token states.

const AuthContextProvider = ({ children }) =&gt; {
  const [userId, setUserId] = useState();
  const [token, setToken] = useState();

  // Effect to initialize local state and reauthenticate
  useEffect(() =&gt; {
    const { userId, token } = JSON.parse(localStorage.getItem(&quot;userData&quot;)) || {};
    if (userId) setUserId(userId);
    if (token) setToken(token);

    if (userId &amp;&amp; token) {
      login(userId, token);
    }
  }, []);

  useEffect(() =&gt; {
    if (token || userId) {
      localStorage.setItem(&quot;userData&quot;, JSON.stringify({ userId, token }));
    } else {
      localStorage.removeItem(&quot;userData&quot;);
    }
  }, [token, userId]);

  const loginUserHandler = useCallback((userId, token) =&gt; {
    setUserId(userId);
    setToken(token);
  }, []);

  const logoutUserHandler = useCallback(() =&gt; {
    setUserId(null);
    setToken(null);
  }, []);

  const initialValues = {
    userId: userId,
    token: token,
    isLoggedIn: !!token,
    login: loginUserHandler,
    logout: logoutUserHandler,
  };

  return (
    &lt;AuthContext.Provider value={initialValues}&gt;
      {children}
    &lt;/AuthContext.Provider&gt;
  );
};

Update the App component to explicitly check if the token is undefined when the component mounts to conditionally return early so neither the "authenticated" or "unauthenticated" routes/redirect logic are applied.

function App() {
  const { token } = useContext(AuthContext);

  if (token === undefined) {
    return null; // or loading indicator/spinner/etc
  }

  return (
    &lt;BrowserRouter&gt;
      {token
        ? (
          &lt;Layout&gt;
            &lt;Switch&gt;
              &lt;Route path=&quot;/home&quot; component={Home} /&gt;
              &lt;Route path=&quot;/leads-manager&quot; component={Leads} /&gt;
              &lt;Route path=&quot;/phone-manager&quot; component={Phone} /&gt;
              &lt;Route path=&quot;/new-notifications&quot; component={Notification} /&gt;
              &lt;Redirect to=&quot;/home&quot; /&gt;
            &lt;/Switch&gt;
          &lt;/Layout&gt;
        )
        : (
          &lt;Switch&gt;
            &lt;Route path=&quot;/sign-in&quot; component={Login} /&gt;
            &lt;Redirect to=&quot;/sign-in&quot; /&gt;
          &lt;/Switch&gt;
        )
      }
    &lt;/BrowserRouter&gt;
  );
}

export default App;

答案2

得分: 0

以下是您提供的代码的翻译:

我稍微修改了代码
我添加了一个额外的状态来检查是否显示`Loader.js`主要用于防止路由重定向到不需要的路由

第一次运行项目`hasInitVal = false`会显示`Loader.js``app.js`组件挂载并执行useEffect函数后`hasInitVal`的值会更改`app.js`会重新加载以显示与令牌值相对应的路由

`autContext.js`文件

```javascript
import { createContext, useCallback, useState } from "react";

export const AuthContext = createContext({
  token: null,
  userId: null,
  isLoggedIn: false,
  login: (userId, token) => {},
  logout: () => {},
});

const AuthContextProvider = (props) => {
  const [userId, setUserId] = useState(null);
  const [token, setToken] = useState(null);

  const loginUserHandler = useCallback((userId, token) => {
    setUserId(userId);
    setToken(token);
    localStorage.setItem("userData", JSON.stringify({ userId, token }));
  }, []);

  const logoutUserHandler = useCallback(() => {
    setUserId(null);
    setToken(null);
    localStorage.removeItem("userData");
  }, []);

  const initialValues = {
    userId: userId,
    token: token,
    isLoggedIn: !!token,
    login: loginUserHandler,
    logout: logoutUserHandler,
  };

  return <AuthContext.Provider value={initialValues}>{props.children}</AuthContext.Provider>;
};

export default AuthContextProvider;

app.js文件

function App() {
  const { token, login } = useContext(AuthContext);
  const [hasInitVal, setHasInitVal] = useState(false);

  useEffect(() => {
    const userToken = JSON.parse(localStorage.getItem("userData"));
    if (userToken && userToken.userId && userToken.token) {
      login(userToken.userId, userToken.token);
    }
    setHasInitVal(true);
  }, [login]);

  return (
    <Fragment>
      {!hasInitVal && <Loader />}
      {!token && hasInitVal && (
        <Router>
          <Routes>
            <Route path="/sign-in" element={<Login />} />
            <Route path="*" element={<Navigate to="/sign-in" />} />
          </Routes>
        </Router>
      )

      {token && hasInitVal && (
        <Router>
          <Routes>
            <Route
              path="/home"
              element={
                <Layout>
                  <TblContextProvider>
                    <Home />
                  </TblContextProvider>
                </Layout>
              }
            />

            <Route
              path="/leads-manager"
              element={
                <Layout>
                  <TblContextProvider>
                    <Leads />
                  </TblContextProvider>
                </Layout>
              }
            />

            <Route
              path="/phone-manager"
              element={
                <Layout>
                  <TblContextProvider>
                    <Phone />
                  </TblContextProvider>
                </Layout>
              }
            />

            <Route path="/new-notifications" element={<Notification />} />
            <Route path="*" element={<Navigate to="/home" />} />
          </Routes>
        </Router>
      )}
    </Fragment>
  );
}

export default App;

希望这些翻译对您有所帮助。如果您有任何其他问题,请随时提出。

英文:

I have changed code a little bit
I added one more state to check display the Loader.js (mainly for preventing route redirection to hit unwanted routes)

First time runs project hasInitVal = false will show the Loader.js. After app.js component mount and useEffect function get executed, value of hasInitVal changed and app.js reload again to display corresponding routes base on the token values.

autContext.js file

import { createContext, useCallback, useState } from &quot;react&quot;;
export const AuthContext = createContext({
token: null,
userId: null,
isLoggedIn: false,
login: (userId, token) =&gt; {},
logout: () =&gt; {},
});
const AuthContextProvider = (props) =&gt; {
const [userId, setUserId] = useState(null);
const [token, setToken] = useState(null);
const loginUserHandler = useCallback((userId, token) =&gt; {
setUserId(userId);
setToken(token);
localStorage.setItem(&quot;userData&quot;, JSON.stringify({ userId, token }));
}, []);
const logoutUserHandler = useCallback(() =&gt; {
setUserId(null);
setToken(null);
localStorage.removeItem(&quot;userData&quot;);
}, []);
const initialValues = {
userId: userId,
token: token,
isLoggedIn: !!token,
login: loginUserHandler,
logout: logoutUserHandler,
};
return &lt;AuthContext.Provider value={initialValues}&gt;{props.children}&lt;/AuthContext.Provider&gt;;
};
export default AuthContextProvider;

app.js file

function App() {
const { token, login } = useContext(AuthContext);
const [hasInitVal, setHasInitVal] = useState(false);
useEffect(() =&gt; {
const userToken = JSON.parse(localStorage.getItem(&quot;userData&quot;));
if (userToken &amp;&amp; userToken.userId &amp;&amp; userToken.token) {
login(userToken.userId, userToken.token);
}
setHasInitVal(true);
}, 
此处为隐藏的内容
注册登录后,方可查看
); return ( &lt;Fragment&gt; {!hasInitVal &amp;&amp; &lt;Loader /&gt;} {!token &amp;&amp; hasInitVal &amp;&amp; ( &lt;Router&gt; &lt;Routes&gt; &lt;Route path=&quot;/sign-in&quot; element={&lt;Login /&gt;} /&gt; &lt;Route path=&quot;*&quot; element={&lt;Navigate to=&quot;/sign-in&quot; /&gt;} /&gt; &lt;/Routes&gt; &lt;/Router&gt; )} {token &amp;&amp; hasInitVal &amp;&amp; ( &lt;Router&gt; &lt;Routes&gt; &lt;Route path=&quot;/home&quot; element={ &lt;Layout&gt; &lt;TblContextProvider&gt; &lt;Home /&gt; &lt;/TblContextProvider&gt; &lt;/Layout&gt; } /&gt; &lt;Route path=&quot;/leads-manager&quot; element={ &lt;Layout&gt; &lt;TblContextProvider&gt; &lt;Leads /&gt; &lt;/TblContextProvider&gt; &lt;/Layout&gt; } /&gt; &lt;Route path=&quot;/phone-manager&quot; element={ &lt;Layout&gt; &lt;TblContextProvider&gt; &lt;Phone /&gt; &lt;/TblContextProvider&gt; &lt;/Layout&gt; } /&gt; &lt;Route path=&quot;/new-notifications&quot; element={&lt;Notification /&gt;} /&gt; &lt;Route path=&quot;*&quot; element={&lt;Navigate to=&quot;/home&quot; /&gt;} /&gt; &lt;/Routes&gt; &lt;/Router&gt; )} &lt;/Fragment&gt; ); } export default App;

huangapple
  • 本文由 发表于 2023年3月9日 23:48:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/75686960.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定