Home > other >  React component getting rerenderd on state change
React component getting rerenderd on state change

Time:01-13

My form was working perfectly earlier and now on after few changes the state change the input filed is loosing focus. I am using MUI and it was working earlier perfectly but suddenly it started losing focus after few minor change in state variable names. I think that is not the issue but i don't know why is this happening as all controlled forms are made like this i presume

import { useEffect, useState } from "react";
import Axios from "axios";
import { Schedule } from "./Schedule";
import { PageHeader } from "./PageHeader";
import { Button, TextField, styled, CircularProgress } from "@mui/material";
import FileUploadIcon from "@mui/icons-material/FileUpload";
import { DesktopDatePicker } from "@mui/x-date-pickers/DesktopDatePicker";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider/LocalizationProvider";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import SendIcon from "@mui/icons-material/Send";

function Form() {
  const [formData, setFormData] = useState({ startTime: "", endTime: "" });
  const [file, setFile] = useState("");
  const [data, setData] = useState("");
  const [newValue, setValue] = useState(null);
  const [loading, setLoading] = useState(false);
  console.log(formData);
  console.log("Rerendered");

  function handleChange(event) {
    setFormData((previousFormData) => {
      return {
        ...previousFormData,
        [event.target.name]: event.target.value,
      };
    });
  }

  function handleClick(event) {
    setLoading(true);
    event.preventDefault();
    setTimeout(() => {
      const postFormData = new FormData();
      postFormData.append("startTime", formData.startTime);
      postFormData.append("endTime", formData.endTime);
      postFormData.append("date", newValue);
      postFormData.append("file", file);

      Axios.post("http://localhost:3001/aip", postFormData, {
        headers: {
          "Access-Control-Allow-Origin": "cors",
        },
      })
        .then((res) => setData(res.data))
        .then(setLoading(false))
        .catch((err) => console.log(err));
    }, 2000);
  }

  const Container = styled("main")({
    marginTop: 50,
    display: "grid",
    justifyItems: "center",
  });
  const Div = styled("div")({
    display: "flex",
    gap: 50,
  });
  const Form = styled("form")({
    border: "none",
    width: "100%",
    height: "100%",
    margin: "auto",
    display: "grid",
    justifyItems: "center",
    gap: 50,
  });

  return (
    <>
      <PageHeader />
      <Container>
        <Form onSubmit={handleClick} encType="multipart/form-data">
          <Div >
            <TextField
              label="From"
              name="startTime"
              variant="outlined"
              placeholder="9:00"
              onChange={handleChange}
              value={formData.startTime}
              required
            />
            <TextField
              label="To"
              name="endTime"
              variant="outlined"
              placeholder="9:30"
              onChange={handleChange}
              value={formData.endTime}
              required
            />
          </Div>
          <div >
            <LocalizationProvider dateAdapter={AdapterDayjs}>
              <DesktopDatePicker
                value={newValue}
                label="Choose Date"
                onChange={(newValue) => setValue(newValue)}
                renderInput={(params) => <TextField {...params} />}
              />
            </LocalizationProvider>
          </div>
          <Div>
            <Button sx={{ gap: 2 }} variant="contained" component="label">
              Upload Schedule
              <input
                hidden
                accept=".xlsx"
                multiple
                type="file"
                onChange={(event) => {
                  const file = event.target.files[0];
                  setFile(file);
                }}
              />
              <FileUploadIcon />
            </Button>
            <Button sx={{ gap: 2 }} variant="contained" component="label">
              Search
              <input hidden type="submit" />
              <SendIcon />
            </Button>
          </Div>
        </Form>
      </Container>
      {loading ? (
        <CircularProgress sx={{ alignItems: "center" }} />
      ) : (
        <Schedule data={data} />
      )}
    </>
  );
}

export default Form;

CodePudding user response:

You should move your styled components outside of the main Form component as they are causing re renders. Just note that you will then have clashing names. Styled Form and the actual component Form. So you will have to either rename the component or the styled Form. As to why this happens, I'm not entirely sure but ran into a similar issue before. I assume that every time you type and the state changes, it's re creating the styled components and then re rendering the main component.

// Imports
import { useEffect, useState } from "react";
import Axios from "axios";
import { Schedule } from "./Schedule";
import { PageHeader } from "./PageHeader";
import { Button, TextField, styled, CircularProgress } from "@mui/material";
import FileUploadIcon from "@mui/icons-material/FileUpload";
import { DesktopDatePicker } from "@mui/x-date-pickers/DesktopDatePicker";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvi/LocalizationProvider";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import SendIcon from "@mui/icons-material/Send";

// Styles
const Container = styled("main")({
  marginTop: 50,
  display: "grid",
  justifyItems: "center",
});
const Div = styled("div")({
  display: "flex",
  gap: 50,
});

// Component
const Form = styled("form")({
  border: "none",
  width: "100%",
  height: "100%",
  margin: "auto",
  display: "grid",
  justifyItems: "center",
  gap: 50,
});

function Form() {
  // ...
}
export default Form;

CodePudding user response:

In this example, whenever the state changes, App will re-render. And because App re-renders, Form gets re-declared and the internal state of the underlying DOM node (form) is lost.

export default function App() {
  const [value, setValue] = useState("");
  const Form = styled("form")({});
  return (
    <Form>
      <input value={value} onChange={({ target }) => setValue(target.value)} />
    </Form>
  );
}

Edit quizzical-forest-ti6bb7

Declare Form outside of the body of App and it works

export default function App() {
  const [value, setValue] = useState("");
  return (
    <Form>
      <input value={value} onChange={({ target }) => setValue(target.value)} />
    </Form>
  );
}

const Form = styled("form")({});

Edit kind-blackburn-lfj8sd

If you must declare Form in the body of App (you probably don't), you can memoise it with React.useMemo

export default function App() {
  const [value, setValue] = useState("");
  const Form = useMemo(() => styled("form")({}), []);
  return (
    <Form>
      <input value={value} onChange={({ target }) => setValue(target.value)} />
    </Form>
  );
}

Edit react FC inside FC with useMemo

  • Related