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>
);
}
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")({});
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>
);
}