0

I am using ReactJs. I am creating a form where I'm inserting the values from the user and and submitting it .

My state is of the form :

{
    firstName: "Magnus",
    lastName: "Effect",
    age: 0,
}

Right now I am using onChange function which takes the input's name and changes the value as shown in below code.

Code

import { Row, Form, Button, Col, Nav, Navbar, Container, NavDropdown, Offcanvas } from "react-bootstrap"
import { useState, useEffect, useRef } from 'react';
import "./App.css";

export default function App() {

  const [credentials, setCredentials] = useState({
    firstName: "",
    lastName: "",
    age: 0,
  });

  const onChange = (e, key) => {
    setCredentials((prevCredentials) => ({
      ...prevCredentials,
      [key]: e.target.value + "",
    }));
    //console.log(credentials);
  };

  const handleSubmit = () => {
    console.log("FIrst Name : ", credentials.firstName);
    console.log("Last Name : ", credentials.lastName);
    console.log("Age : ", credentials.age);
  }

  const addAge = () => {
    // only want to set the age to age+3
    
    //I tried this but NOT working 
    //setCredentials(credentials.age + 3);
    
    console.log("New Age : ", credentials.age);
  }


  return <div style={{ padding: "15px" }}>


    <h1>Form</h1>

    <div className="container" >
      <div className='col-7 mx-auto border border-1 rounded-5 bg-light border-dark p-5 mb-4'>

        <Form className="fs-4">


          <Form.Group
            as={Row}
            className="mb-3"
            controlId="formBasicText"
          >
            <Form.Label column sm="4">
              First Name
            </Form.Label>
            <Col>:</Col>
            <Col sm="7">
              <Form.Control
                type="text"
                value={credentials.firstName}
                onChange={(e) => onChange(e, "firstName")}
                placeholder="Enter Name here..."
              />
            </Col>
          </Form.Group>

          <Form.Group
            as={Row}
            className="mb-3"
            controlId="formBasicText"
          >
            <Form.Label column sm="4">
              Last Name
            </Form.Label>
            <Col>:</Col>
            <Col sm="7">
              <Form.Control
                type="text"
                value={credentials.lastName}
                onChange={(e) => onChange(e, "lastName")}
                placeholder="Enter Last Name here..."
              />
            </Col>
          </Form.Group>

          <Form.Group
            as={Row}
            className="mb-3"
            controlId="formBasicText"
          >
            <Form.Label column sm="4">
              Age
            </Form.Label>
            <Col>:</Col>
            <Col sm="7">
              <Form.Control
                type="number"
                value={credentials.age}
                onChange={(e) => onChange(e, "age")}
                placeholder="Enter Age here..."
              />
            </Col>
          </Form.Group>





          <Row sm={6} className="justify-content-md-center my-3">
            <Col>
              <Button
                variant="primary"
                type="submit"

                onClick={handleSubmit}
                size="lg"
              >
                Submit
              </Button>
            </Col>

            <Col sm={8}>
              <Button
                variant="primary"
                type="submit"

                onClick={() => addAge()}
                size="lg"
              >
                Add 3 years to age.
              </Button>
            </Col>

          </Row>
        </Form>

      </div>
    </div>


  </div>
}

I want that after pressing the Add 3 yrs age should update the credentials.age state only . How can I do so?

I had tried

setCredentials(credentials.age + 3);

But it's not working?

Find live code here

MagnusEffect
  • 3,363
  • 1
  • 16
  • 41
  • 3
    You already did it in your `onChange`, just make it specific for age. `setCredentials(prevCredentials => ({...prevCredentials, age: prevCredentials.age + 3}))`. Keep in mind that logging directly after a `setState` call won't show the updated state, the new state won't be available until the next render cycle. see: [The useState set method is not reflecting a change immediately](https://stackoverflow.com/questions/54069253/the-usestate-set-method-is-not-reflecting-a-change-immediately) – pilchard Apr 02 '23 at 10:58

1 Answers1

0

The setCredentials function updates the entire credentials object, so you need to copy the credentials object and override just the property that changed on the copy.

setCredentials({...credentials, age: credentials.age + 3};

Although, since you're reading the previous state as part of forming the value of the new state, you should actually pass in a fuction to setCredentials instead like so:

setCredentials(oldState => {

    return {...oldState, age: oldState.age + 3}
});

The reason for this is that React batches state updates so for example, if age is 0 and two events happen that both want to increase age by 3, you want the final age to be 6, but if they get batched together, they will both do 0 + 3 and your final age will be 3, not 6.

Passing in a function to setCredentials fixes this. The function takes in the latest state, including the results of previous update operations in the batch, so if two events were processed in a single batch, the first would see oldState.age as having the value 0 and the second would see oldState.age as having the value 3.

Hope that helps! This is a pretty fundamental feature of React, I recommend going through a React course if you're having trouble here, good luck!

Roy
  • 31
  • 3