0

I want to call some function whenever a user enters some value in text boxes. Here is my code using React Hooks:

function MyApp() {
    const [name, setName] = React.useState("");
    const [city, setType] = React.useState("");
    const [completion, setCompletion] = useState(0);
    
    calculateProgress = () => {
        let v1 = name;
        let v2 = city;
        let total = 0;
        if(v1.length >0 ) total += 50;
        if(v2.length >0 ) total += 50;
        setCompletion(total);
    }
    
    return {
        <div>
            <input type="name" onChange={(e) => setName(e.target.value); calculateProgress();}
            <input type="city" onChange={(e) => setCity(e.target.value); calculateProgress();}
        </div>
    }
}

When I enter some value in name or city calculateProgress should be called. But in my case, the value for name or city is empty inside calculateProgress method whenever I type something. What is the correct way to do this?

learner
  • 6,062
  • 14
  • 79
  • 139
  • By the time `calculateProgress` is called the state has not changed yet for the `city` or `name` state. You will have to pass in the values into your function there. – Dominik Jul 06 '21 at 21:36

1 Answers1

1

Like I said in my comment, by the time calculateProgress is called the state has not changed yet for the city or name variables. React can't split a function in half and refresh the state half way through. It will run your onChange function entirely before the component state is refreshed with the new value.

You will have to pass in the values into your function there:

function MyApp() {
    const [name, setName] = React.useState("");
    const [city, setType] = React.useState("");
    const [completion, setCompletion] = useState(0);
    
    calculateProgress = (name, city) => {
        let v1 = name;
        let v2 = city;
        let total = 0;
        if(v1.length >0 ) total += 50;
        if(v2.length >0 ) total += 50;
        setCompletion(total);
    }

    return {
        <div>
            <input type="name" onChange={(e) => setName(e.target.value); calculateProgress(e.target.value, city);}
            <input type="city" onChange={(e) => setCity(e.target.value); calculateProgress(name, e.target.value);}
        </div>
    }
}

Note: For simplicity I made the changes as minimal as possible but I would suggest to also make a wrapping function for those onChange handlers and perhaps rename the new name and city variables for inside the calculateProgress function to make it clear what they are (or rather what they are not)

Dominik
  • 6,078
  • 8
  • 37
  • 61
  • Thank you, another question - in my code I have more fields like age, state, firstname, lastname etc. so how can I optimize the code in better way, i mean instead of setFirstName, setLastName, setAge and then again passing all values to calculateProgrss function is bad idea. I am new to react so need your help – learner Jul 06 '21 at 21:42
  • This is probably worth a new question on SO but off the cuff from me I would say you create a handler function for the onChange and pass in the reference WHAT is being changed and the new value then in the function you look at what and depending on what set the state and calculate the progress dynamically. So your onChange would look like this: `onChange={() => handleChange('city', e.target.value)}` for city or `onChange={() => handleChange('name', e.target.value)}` for name etc – Dominik Jul 06 '21 at 21:46