Dynamically Calculate Input values - React

Hello ,
I am working on a form that is filled by the user and should update other input fields at the same time according to the entered data. For instance, If we have 3 input fields of number 1, number 2 and their sum. How can we update each one of them as the user types? The problem with my current implementation is that it does not always work. If a field id rendered automatically and I try to change it manually, everything stops. In other words, the app is not taking into consideration the values calculated from the formulas, they are just rendered in the fields. Thank you for your time!

import React, { useState } from 'react';
const Calculations = () => {

const [values,setValues]=useState({first:"",second:"",sum:""})
const [first,setFirst]=useState('')
const [second,setSecond]=useState('')
const [sum,setSum]=useState('')
const onChange=(e)=>{
    let name=e.target.name;
    let value=e.target.value;
    const newValues = {
    ...values,
    [name]: value
} 
setValues(newValues)
calcSum(newValues)
calcfirst(newValues)
calcSecond(newValues)


}
const calcSum = (newValues) => {
const { first,second} = newValues;
const newSum = parseInt(first,10)+parseInt(second,10)
setSum(newSum)
} 
const calcfirst = (newValues) => {
const { sum,second} = newValues;
const newFirst = parseInt(sum,10)-parseInt(second,10)
setFirst(newFirst)
} 
const calcSecond = (newValues) => {
const { sum,first} = newValues;
const newSecond = parseInt(sum,10)-parseInt(first,10)
setSecond(newSecond)
} 

return ( <form>
       <div style={{display:"flex",flexDirection:"column"}}>
        <label htmlFor="first">First</label>
        <input onChange={onChange} defaultValue={first} name='first' id="first" type="number"/>

        <label htmlFor="second">Second</label>
        <input onChange={onChange} defaultValue={second} name="second"  id="second" type="number"/>

        <label htmlFor="sum">Total</label>
        <input onChange={onChange} defaultValue={sum} id="sum" name="sum" type="number"/>


       </div>
    </form> );
}

export default Calculations;

Hi @malekhammou,

Did I understand you correctly - You have 3 fields. You want to type something to one of them and get updates to others based on the input?

Or is it so that you have a top-level input field, and by updating it you want these 3 fields to update accordingly?

The former! I want to type something to one of them and get updates to others based on the input. For example, when I type the sum and the second number, I need the field of the first number to be updated and vice versa.

@malekhammou Okay, let’s suppose you have all 3 fields populated (2,2, and the sum is 4).

What will happen if you change the sum field to 1? How the program should understand what field should be updated to 0 and what field to 1?

Do you understand the problem here?
IMO, it is better to re-think the approach you are trying to implement as you can simplify it :slight_smile:

The particular case scenario you mentioned is going to be handled later through form validation. If the sum field is not equal to the sum of the other fields the user will receive an error on submit. My problem is the coherent update of the state and input fields. The app should be listening to different updates and recalculating what can be calculated (Addition, substitution, etc…).

@malekhammou thanks for the explanation.

Please, take a look at my solution and don’t hesitate to ask questions if something is unclear. Just in case, you need to install a dependency npm i react-debounce-input to make it work.

import { DebounceInput } from 'react-debounce-input';
import React from 'react';

const SUBTRACT_A = 'SUBTRACT_A';
const SUBTRACT_B = 'SUBTRACT_B';
const SUM = 'SUM';

function reducer(state, action) {
  switch (action.type) {
    case SUBTRACT_A:
      return { ...state, a: action.payload, sum: state.sum - action.payload };
    case SUBTRACT_B:
      return { ...state, b: action.payload, sum: state.sum - action.payload };
    case SUM:
      return { sum: action.payload, a: action.payload / 2, b: action.payload / 2 };
    default:
      throw new Error();
  }
}

const App = () => {
  const [state, dispatch] = React.useReducer(reducer, { a: 0, b: 0, sum: 0 });

  return (
    <form>
      <div style={{ display: 'flex', flexDirection: 'column' }}>
        <label htmlFor='first'>A</label>
        <DebounceInput
          onChange={(e) => dispatch({ type: SUBTRACT_A, payload: +e.target.value })}
          value={state.a || ''}
          debounceTimeout={300}
          name='a'
        />

        <label htmlFor='second'>B</label>
        <DebounceInput
          onChange={(e) => dispatch({ type: SUBTRACT_B, payload: +e.target.value })}
          value={state.b || ''}
          debounceTimeout={300}
          name='b'
        />

        <label htmlFor='sum'>Total</label>
        <DebounceInput
          onChange={(e) => dispatch({ type: SUM, payload: +e.target.value })}
          value={state.sum || ''}
          debounceTimeout={300}
          name='sum'
        />
      </div>
    </form>
  );
};

export default App;

I can’t thank you enough! You solved my problem. In fact, I am dealing with 9 input fields and many of them are interconnected by some formulas. I shared this problem for simplicity ends. Your solution can be adapted for any number of inputs if you implement some priority logic.
Again, Thank you very much.

1 Like