I am trying to create a multi step register form for my app. I am saving form data to a "registerData" state.
I have a problem with inputting data. Whenever I try to input data in one of the component pages (for example page1, Name), it does the onChange and changes the Name from "" to inputted letter but immediately after that it reverts "registerData" to its original state with all properties set to "".
Here is the code:
Register.js:
import React, {useState} from 'react';
import Page1 from './register_components/Page1';
import Page2 from './register_components/Page2';
import Page3 from './register_components/Page3';
function Register() {
const [registerData, setRegisterData] = useState(
{
//Personal Details
Name: "",
LastName: "",
Phone: "", //Null
Birthday: "", //Null
//Address
Street: "",
Zip: "",
Building: "",
House: "", //Null
City: "",
Voivodeship: "",
//Login information
Email: "",
Password: ""
}
)
const [ pageIndex, setPageIndex] = useState(0)
const [ pages, setPages ] = useState([
<Page1 registerData={registerData} setRegisterData={(data) => {setRegisterData(data)}}/>,
<Page2 registerData={registerData} setRegisterData={(data) => {setRegisterData(data)}}/>,
<Page3 registerData={registerData} setRegisterData={(data) => {setRegisterData(data)}}/>
])
const PrevPage = () =>{
if (pageIndex <= 0)
return
setPageIndex(pageIndex - 1)
}
const HandleSubmit = (event) => {
event.preventDefault();
if (pageIndex !== pages.length - 1){
setPageIndex(pageIndex 1)
return
}
console.log("Submmited")
}
return (
<div className="App">
<h1>Rejestracja</h1>
<form className='register-form' onSubmit={HandleSubmit}>
{pages[pageIndex]}
{pageIndex !== 0 && <button type="button" onClick={ () => {PrevPage()}}>Poprzedni</button>}
<button type="submit">{pageIndex !== pages.length - 1? "Nastepny" : "Przeslij"}</button>
</form>
</div>
);
}
export default Register;
Page1.js
import React from 'react'
export default function Page1({registerData, setRegisterData}) {
console.log("rerender")
console.log(registerData)
return (
<div>
<input
placeholder='Imie'
required type='text'
value={registerData.Name}
onChange={(e) => { setRegisterData({...registerData, Name: e.target.value}); console.log(e.target.value) }}
/>
<input
placeholder='Nazwisko'
required
type='text'
value={registerData.LastName}
onChange={(e) => { setRegisterData({...registerData, LastName: e.target.value}) }}
/>
<input
placeholder='Nr telefonu'
required
type='text'
value={registerData.Phone}
onChange={(e) => { setRegisterData({...registerData, Phone: e.target.value}) }}
/>
<div className='date-form'>
<label>Data urodzenia</label>
<input
type='date'
value={registerData.Date}
onChange={e => { setRegisterData({...registerData, Birthday: e.target.value}) }}
/>
</div>
</div>
)
}
As I don't have much experience with react, it's hard for me to tell but I think the problem lays in the Register.js rerendering with each change to the "registerData" state which causes it to revert to its original values.
CodePudding user response:
Try this as a function for onChange values... It helps you a lot. Secondly, in you input tag add the name attribute with the same state object names.
For Example:
const [registerData, setRegisterData] = useState(
{
//Personal Details
Name: "",
LastName: "",
Phone: "", //Null
Birthday: "", //Null
//Address
Street: "",
Zip: "",
Building: "",
House: "", //Null
City: "",
Voivodeship: "",
//Login information
Email: "",
Password: ""
}
)
const handleFormChange = (index, event) => {
let data = [...inputFields];
data[index][event.target.name] = event.target.value;
setInputFields(data);
};
<div>
<input
placeholder='Imie'
required type='text'
name="Name"
value={registerData.Name}
onChange={(event) => handleFormChange(index, event)}
/>
<input
placeholder='Nazwisko'
required
type='text'
name="LastName"
value={registerData.LastName}
onChange={(event) => handleFormChange(index, event)}
/>
</div>
</div>
CodePudding user response:
You shouldn't store JSX in state. Your pages
state isn't a state value, as it can be calculated by other state values in this case (pageIndex
). Another thing that hints that it shouldn't be a state value is that you're never using setPages()
. Instead, you can make this a regular object:
const pages = [
<Page1 registerData={registerData} setRegisterData={(data) => {setRegisterData(data)}} />,
<Page2 registerData={registerData} setRegisterData={(data) => {setRegisterData(data)}} />,
<Page3 registerData={registerData} setRegisterData={(data) => {setRegisterData(data)}} />
];
I would also consider putting the above into its own component called Page
which accepts a page number as an index and then uses that number in a simple if-statement to decide which page component (Page1
, Page2
, Page3
) to render.
So, why does making pages
a regular object instead of a state value fix this issue? Each time your Register
component rerenders, the code within Register
runs, causing the line with useState()
to execute again:
const [pages, setPages] = useState(/* initival value */ [...])
The "initial value" argument that you pass to useState()
is ignored after the initial render, so while the "initial value" gets evaluated each time your component rerenders, it gets ignored and the value of pages
doesn't change, only using setPages()
can change pages
. This results in pages
storing an array of object references that represent your JSX on the initial mount of your component. That means that on subsequent rerenders of your component, pages[pageIndex]
will always return the same JSX object reference, and as it's the same on every rerender, React won't rerender this component.