React Context и hookrouter не перенаправляют при монтировании компонента - PullRequest
1 голос
/ 20 сентября 2019

Я пытаюсь «скопировать» мой реальный проект с помощью CodeSandbox ( здесь ).У меня есть форма входа в систему, и при входе она устанавливает cookie с данными аутентификации.Когда компонент <LoginPage> монтируется, он должен проверить этот файл cookie и перенаправить обратно на компонент <HomePage>, если таковой существует.

В моем случае это не работает должным образом, так как ожидает, пока я что-то не напишу вформа <LoginPage>.

В соответствии с документацией hookrouter navigate() должен работать как компонент response-router-dom <Redirect />.

Итак, этопроект src структура папок:

  • src
    • index.js
    • компоненты
      • страницы
        • Home
          • index.js
        • Логин
          • index.js
    • context
      • Auth
        • auth.js
        • index.js

Код:

App: src / index.js

import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import { useRoutes, A, navigate } from "hookrouter";
import { useCookies } from "react-cookie";

import HomePage from "./components/pages/Home";
import LoginPage from "./components/pages/Login";

import AuthContextWrapper from "./context/Auth";

const Logout = () => {
  const [cookies, setCookie, removeCookie] = useCookies(["token"]);
  useEffect(() => {
    console.log("Logout triggered");
    removeCookie("token");
    navigate("/", true);
  }, []);
  return <p>Closing session</p>;
};

const routes = {
  "/": () => <HomePage />,
  "/login": () => <LoginPage />,
  "/logout": () => <Logout />
};

const App = () => {
  const Router = useRoutes(routes);
  return Router ? (
    <AuthContextWrapper>{Router}</AuthContextWrapper>
  ) : (
    <h1>
      Error 404: <A href="/">Go back</A>
    </h1>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

<HomePage />: src / components / pages / Home / index.js

import React, { Fragment, useContext } from "react";
import { A } from "hookrouter";
import AuthContext from "../../../context/Auth/auth";

const HomePage = () => {
  const context = useContext(AuthContext);
  console.log("Home context", context);
  return (
    <Fragment>
      <A href="/login">Login page</A>
      {context.token.length ? <A href="/logout">Logout</A> : ""}
    </Fragment>
  );
};

export default HomePage;

<LoginPage />: src / components / pages / Login / index.js

import React, { useState, useContext, useEffect } from "react";
import AuthContext from "../../../context/Auth/auth";
import { navigate } from "hookrouter";

const LoginPage = () => {
  const context = useContext(AuthContext);
  const [emailState, setEmailState] = useState("");
  const [passwordState, setPasswordState] = useState("");
  const [statusState, setStatusState] = useState({
    color: "black",
    status: ""
  });

  useEffect(() => {
    console.log("Login context:", context);
    if (context.token) {
      navigate("/");
    }
  }, []);

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={emailState}
        onChange={e => setEmailState(e.target.value)}
        placeholder="Email"
      />
      <input
        value={passwordState}
        onChange={e => setPasswordState(e.target.value)}
        placeholder="Password"
        type="password"
      />
      <p style={{ color: statusState.color }}>{statusState.status}</p>
      <button>Login</button>
    </form>
  );

  async function handleSubmit(e) {
    e.preventDefault();
    setStatusState({
      color: "blue",
      status: "Validating"
    });
    await context.login(emailState, passwordState);
    if (context.error) {
      setStatusState({
        color: "red",
        status: context.error
      });
    }
    if (context.token) {
      navigate("/");
    }
  }
};

export default LoginPage;

<AuthContextWrapper />: src / context / Auth / index.js

import React, { useEffect } from "react";
import Context from "./auth";
import { useCookies } from "react-cookie";

const AuthContextWrapper = props => {
  const [cookies, setCookie] = useCookies(["token"]);
  const defaultValue = {
    token: "",
    error: "",
    loading: false,
    login: async (email, password) => {
      setTimeout(() => {
        defaultValue.loading = true;
        if (password !== "123") {
          defaultValue.error = "Wrong credentials";
        } else {
          defaultValue.error = "";
          defaultValue.token = "jwtencodedtoken$123";
          setCookie("token", defaultValue.token);
        }
      }, 1000);
      defaultValue.loading = false;
    }
  };
  useEffect(() => {
    if (cookies.token) {
      defaultValue.token = cookies.token;
    }
  });
  return (
    <Context.Provider value={defaultValue}>{props.children}</Context.Provider>
  );
};

export default AuthContextWrapper;

Итак, я думаю, моя голова только что взорвалась ... Это живой пример , живущий внутри CodeSandbox.Возможно, что-то не так с крючками (я все еще учу их).

Почему это не работает?Что я делаю неправильно?Любые комментарии приветствуются.

Ответы [ 2 ]

2 голосов
/ 20 сентября 2019

Хорошо, я обновил ваш код песочницы здесь .

Изменения на src/context/Auth/index.js

Ваш поставщик контекста был немного неуклюжим.У вас будет плохое время, если вы попытаетесь просто манипулировать объектом напрямую, как это с помощью вашей функции входа в систему.Если вы хотите видеть обновления, свяжите их с состоянием и передайте их как значения вашему провайдеру.

Меняется на src/components/pages/Login/index.js

Необязательно, чтобы эта функция handleSubmit была асинхронной, а также то, как вы ее настраиваете, может быть проблематично, не делая функцию входа в систему обещанием.В итоге вы возвращаетесь рано, а не по истечении времени ожидания.

Вам лучше реагировать на эти обновления контекста внутри хука useEffect с правильными зависимостями .Таким образом, вы можете делать необходимые обновления в этом компоненте всякий раз, когда обновляется контекст.

1 голос
/ 20 сентября 2019

Это глупое решение, ответьте на вопрос @ apachuilo.Просто оставив его здесь для справки, и потому что мне показалось интересным, что добавление тайм-аута как-то заставляет его работать

Проблема заключается в том, что значение context.token оценивается как пустое в хуке useEffect функции login.Если вы добавите тайм-аут в функцию useEffect, у нее будет время для получения значения контекста, и ваше перенаправление будет работать:

useEffect(() => {
  const timer = setTimeout(() => {
    if (context.token) {
      navigate("/");
    }
  }, 100);
  return () => clearTimeout(timer);
}, []);
...