反应:输入失去焦点,输入字符,

问题描述 投票:0回答:1

厌倦玩react

使用 MUI、formik 和 yup 创建一个登录页面,将电子邮件和密码作为输入字段 但每当我尝试在一个字符后输入任何文本时,输入就会失去焦点。其他一切都按预期进行。

如果我将这些代码移至 app.js 则没有问题,但将其移回 login.jsx 似乎根本不起作用。

下面是我的代码

app.js

import {
  Navigate,
  RouterProvider,
  createBrowserRouter,
} from "react-router-dom";
import { ColorModeContext, useMode } from "./theme";
import Login from "./pages/login/Login";
import { useContext, useState } from "react";
import { AuthContext } from "./context/AuthContext";
import { Box, CssBaseline, ThemeProvider } from "@mui/material";
import Home from "./pages/home/Home";
import ErrorPage from "./pages/error/Error";
import SidebarComponent from "./components/sidebar/Sidebar";
import Register from "./pages/register/Register";

function App() {
  const currentUser = useContext(AuthContext);
  const [theme, colorMode] = useMode();

  const Layout = () => {
    <Box
      sx={{
        display: "flex",
        height: "100%",
        minHeight: "400px",
      }}
    >
      <SidebarComponent />

      <main>rtest</main>
    </Box>;
  };

  const ProtectedRoute = ({ children }) => {
    if (!currentUser.user) {
      return <Navigate to="/login" />;
    }

    return children;
  };

  const router = createBrowserRouter([
    {
      path: "/",
      element: (
        <ProtectedRoute>
          <Layout />
        </ProtectedRoute>
      ),
      children: [
        {
          path: "/",
          element: <Home />,
        },
      ],
      errorElement: <ErrorPage />,
    },
    {
      path: "/login",
      element: <Login />,
    },
    {
      path: "/login",
      element: <Register />,
    },
  ]);

  return (
    <ColorModeContext.Provider value={colorMode}>
      <ThemeProvider theme={theme}>
        <CssBaseline />
        <div className="App">
          <RouterProvider router={router} />
        </div>
      </ThemeProvider>
    </ColorModeContext.Provider>
  );
}

export default App;

登录.jsx

import Logo from "../../assets/backticklogo.svg";
import { Link, useNavigate } from "react-router-dom";
import { useContext, useEffect, useState } from "react";
import { AuthContext } from "../../context/AuthContext";
import apiClient from "../../services/api";
import { Box, Button, Stack, TextField, Typography } from "@mui/material";
import styled from "@emotion/styled";
import { useFormik } from "formik";
import * as yup from "yup";

const validationSchema = yup.object({
  email: yup
    .string("Enter your email")
    .email("Enter a valid email")
    .required("Email is required"),
  password: yup
    .string("Enter your password")
    .min(8, "Password should be of minimum 8 characters length")
    .required("Password is required"),
});

const Login = () => {
  const localplainTextToken = localStorage.getItem("plainTextToken") || null;
  const [isAuth, setIsAuth] = useState(localStorage.getItem("isAuth") || null);

  const [plainTextToken, setplainTextToken] = useState(localplainTextToken);
  const remember = true;
  const navigate = useNavigate();
  const { user, dispatch } = useContext(AuthContext);

  useEffect(() => {
    isAuth && navigate("/");
  }, [isAuth]);

  const handleUpdateUser = (userInfo) => {
    console.log(userInfo);
    dispatch({ type: "LOGIN", payload: userInfo });
    localStorage.setItem("user", JSON.stringify(userInfo));
    localStorage.setItem("isAuth", true);
    setIsAuth(true);
  };

  const formik = useFormik({
    initialValues: {
      email: "",
      password: "",
    },
    validationSchema: validationSchema,
    onSubmit: async (values) => {
      // alert(JSON.stringify(values, null, 2));
      try {
        await apiClient
          .post("login", {
            email: values.email,
            password: values.password,
          })
          .then((res) => {
            console.log(res);
            localStorage.setItem("plainTextToken", res.data.plainTextToken);
            setplainTextToken(res.data.plainTextToken);
            return res.data;
          })
          .then(async (data) => {
            try {
              let user = await apiClient.get(`user/${data.user_id}`, {
                headers: {
                  Authorization: `Bearer ${data.plainTextToken}`,
                  Accept: "application/json",
                },
              });
              console.log(user);
              handleUpdateUser(user.data);
              // navigate("/");
            } catch (error) {}
          });
      } catch (error) {
        console.log(error);
      }
    },
  });

  /***********Styled component */
  const LoginContainer = styled(Box)(({ theme }) => ({
    display: "flex",
    flex: "1 1 auto",
    height: "100%",
    width: "100%",

    [theme.breakpoints.up("xs")]: {
      flexDirection: "column-reverse",
    },
    [theme.breakpoints.up("md")]: {
      flexDirection: "row",
    },
  }));

  const RightBox = styled(Box)(({ theme }) => ({
    backgroundColor: "rgb(14, 8, 39)",
    display: "flex",
    flexDirection: "column",
    maxWidth: "100%",

    [theme.breakpoints.up("xs")]: {
      flex: "1 1 auto",
      padding: "32px",
    },
    [theme.breakpoints.up("md")]: {
      flex: "0 0 auto",
      justifyContent: "center",
      padding: "64px",
      width: "600px",
    },
  }));

  const LeftBox = styled(Box)(({ theme }) => ({
    display: "flex",
    alignItems: "center",
    backgroundColor: "rgb(28, 37, 54)",
    backgroundImage: "url(/assets/gradient-bg.svg)",
    backgroundPosition: "center top",
    backgroundRepeat: "no-repeat",
    color: "rgb(255, 255, 255)",
    justifyContent: "center",

    [theme.breakpoints.up("xs")]: {
      flex: "0 0 auto",
      padding: "32px",
    },
    [theme.breakpoints.up("md")]: {
      flex: "1 1 auto",
      padding: "64px",
    },
  }));

  return (
    <LoginContainer>
      <LeftBox>
        <Box sx={{ maxWidth: "900px" }}>
          <Typography variant="h4"> Welcome to Backtic</Typography>
          <Typography sx={{ color: "rgb(108, 115, 127)" }}>
            Some cool text here to represent out Backtick
          </Typography>
        </Box>
      </LeftBox>
      <RightBox>
        <div>
          <Box mb={"32px"}>
            <Link to="/">
              <img src={Logo} alt="Backtick Logo" width={140} />
            </Link>
          </Box>
        </div>
        <div>
          <Stack direction="column" mb={"32px"}>
            <Typography variant="h4" sx={{ color: "rgb(108, 115, 127)" }}>
              Login
            </Typography>
            <Typography sx={{ color: "rgb(108, 115, 127)" }}>
              Don't have an account? <Link to="/register">Register</Link>
            </Typography>
          </Stack>

          <form onSubmit={formik.handleSubmit}>
            <Stack direction="column">
              <TextField
                fullWidth
                id="email"
                name="email"
                label="Email"
                value={formik.values.email}
                onChange={formik.handleChange}
                onBlur={formik.handleBlur}
                error={formik.touched.email && Boolean(formik.errors.email)}
                helperText={formik.touched.email && formik.errors.email}
              />
              <TextField
                fullWidth
                id="password"
                name="password"
                label="Password"
                type="password"
                value={formik.values.password}
                onChange={formik.handleChange}
                onBlur={formik.handleBlur}
                error={
                  formik.touched.password && Boolean(formik.errors.password)
                }
                helperText={formik.touched.password && formik.errors.password}
                sx={{ marginTop: "24px" }}
              />
            </Stack>
            <Button
              color="primary"
              variant="contained"
              fullWidth
              type="submit"
              size="large"
              sx={{ marginTop: "24px" }}
            >
              Submit
            </Button>
          </form>
        </div>
      </RightBox>
    </LoginContainer>
  );
};

export default Login;
reactjs material-ui react-router formik yup
1个回答
0
投票

问题是您在其他 React 组件中声明 React 组件。每次

Login
由于状态更新而重新渲染时,都会重新声明样式组件。这具有卸载旧“实例”及其整个子 ReactTree,并安装新“实例”和子 ReactTree 的最终效果。

将无关的组件声明移出

Login
组件。

/***********Styled component */
const LoginContainer = styled(Box)(({ theme }) => ({
  display: "flex",
  flex: "1 1 auto",
  height: "100%",
  width: "100%",

  [theme.breakpoints.up("xs")]: {
    flexDirection: "column-reverse",
  },
  [theme.breakpoints.up("md")]: {
    flexDirection: "row",
  },
}));

const RightBox = styled(Box)(({ theme }) => ({
  backgroundColor: "rgb(14, 8, 39)",
  display: "flex",
  flexDirection: "column",
  maxWidth: "100%",

  [theme.breakpoints.up("xs")]: {
    flex: "1 1 auto",
    padding: "32px",
  },
  [theme.breakpoints.up("md")]: {
    flex: "0 0 auto",
    justifyContent: "center",
    padding: "64px",
    width: "600px",
  },
}));

const LeftBox = styled(Box)(({ theme }) => ({
  display: "flex",
  alignItems: "center",
  backgroundColor: "rgb(28, 37, 54)",
  backgroundImage: "url(/assets/gradient-bg.svg)",
  backgroundPosition: "center top",
  backgroundRepeat: "no-repeat",
  color: "rgb(255, 255, 255)",
  justifyContent: "center",

  [theme.breakpoints.up("xs")]: {
    flex: "0 0 auto",
    padding: "32px",
  },
  [theme.breakpoints.up("md")]: {
    flex: "1 1 auto",
    padding: "64px",
  },
}));
const Login = () => {
  ...

  return (
    <LoginContainer>
      <LeftBox>
        <Box sx={{ maxWidth: "900px" }}>
          <Typography variant="h4"> Welcome to Backtic</Typography>
          <Typography sx={{ color: "rgb(108, 115, 127)" }}>
            Some cool text here to represent out Backtick
          </Typography>
        </Box>
      </LeftBox>
      <RightBox>
        <div>
          <Box mb={"32px"}>
            <Link to="/">
              <img src={Logo} alt="Backtick Logo" width={140} />
            </Link>
          </Box>
        </div>
        <div>
          <Stack direction="column" mb={"32px"}>
            <Typography variant="h4" sx={{ color: "rgb(108, 115, 127)" }}>
              Login
            </Typography>
            <Typography sx={{ color: "rgb(108, 115, 127)" }}>
              Don't have an account? <Link to="/register">Register</Link>
            </Typography>
          </Stack>

          <form onSubmit={formik.handleSubmit}>
            <Stack direction="column">
              <TextField
                fullWidth
                id="email"
                name="email"
                label="Email"
                value={formik.values.email}
                onChange={formik.handleChange}
                onBlur={formik.handleBlur}
                error={formik.touched.email && Boolean(formik.errors.email)}
                helperText={formik.touched.email && formik.errors.email}
              />
              <TextField
                fullWidth
                id="password"
                name="password"
                label="Password"
                type="password"
                value={formik.values.password}
                onChange={formik.handleChange}
                onBlur={formik.handleBlur}
                error={
                  formik.touched.password && Boolean(formik.errors.password)
                }
                helperText={formik.touched.password && formik.errors.password}
                sx={{ marginTop: "24px" }}
              />
            </Stack>
            <Button
              color="primary"
              variant="contained"
              fullWidth
              type="submit"
              size="large"
              sx={{ marginTop: "24px" }}
            >
              Submit
            </Button>
          </form>
        </div>
      </RightBox>
    </LoginContainer>
  );
};

export default Login;
© www.soinside.com 2019 - 2024. All rights reserved.