简体   繁体   中英

How to stop State object from updating with onChange of an input field in React js

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.

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>

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM