1

I am making a React application for a currency converter website. I have two <input> fields where the user can input the number to get the exchange rate as shown here: Screenshot of Component

The <input> fields are as follows:

<>
  <input
    id="1"
    placeholder="0"
    type="number"
    value={state1.value}
    onChange={handleChange}
    disabled={loading}
  />

// rest of the component

  <input
    id="2"
    placeholder="0"
    type="number"
    value={state2.value}
    onChange={handleChange}
    disabled={loading}
  />

The states for the input fields are:

const [state1, setState1] = useState({
    value: '',
    currency: 'USD',
    src: 'https://hatscripts.github.io/circle-flags/flags/us.svg',
});
const [state2, setState2] = useState({
    value: '',
    currency: 'INR',
    src: 'https://hatscripts.github.io/circle-flags/flags/in.svg',
});

I want the state2 to be updated when the user types in state1. And vice-versa. I don't want the auto-update of state2 again changing state1. The state change should happen only when the user gives input.

I have tried using session state to avoid unnecessary API calls. It solves the problem except for the infinite recursion.

I have already written a useEffect for state1 which works as expected. Now I want to do the same for state2 without an infinite useEffect loop.

async function fetchCurrencyRate1(from, to) {
    setLoading(true);
    try {
        const response = await axios.get('http://localhost:4000/convert', {
            params: {
                from: from.currency,
                to: to.currency,
            },
        });
        setState2((state2) => ({
            ...state2,
            value: (from.value * response.data.value).toFixed(4),
        }));
        console.log(response.data);
    } catch (error) {
        console.error(error);
    }
    setLoading(false);
}

useEffect(() => {
    setTimeout(() => {
        state1.value && fetchCurrencyRate1(state1, { ...state2 });
        if (state1.value === '') setState2({ ...state2, value: '' });
    }, 2000);
}, [state1]);
Ahmed Sbai
  • 10,695
  • 9
  • 19
  • 38
Amrut Wali
  • 13
  • 3

1 Answers1

0

The infinite render occurs because the useEffect of state1 will fire each time state1 take a new value and since inside the effect you update state2 and this triggers useEffect of state2 and the code there updates state1 then useEffect of state1 will be called infinitely because state1 is an object so its value is always new.

you don't need useEffect for that, also you don't want to rerender the component twice on each input change you can simply make two separate handleChange functions for each input and make your logic in each one of them
something like this:

const handleChangeOne = async (e) => {
  // you can fetch data here instead
  setState1((prev) => {
    return {
      ...prev,
      value: e.target.value,
    };
  });
  setState2((prev) => {
    return {
      ...prev,
      value: (from.value * response.data.value).toFixed(4), 
    };
  });
};
const handleChangeTwo = async (e) => {
  // fetch data
  setState2((prev) => {
    return {
      ...prev,
      value: Number(e.target.value),
    };
  });
  setState1((prev) => {
    //
    return {
      ...prev,
      value: (from.value * response.data.value).toFixed(4),
    };
  });
};
Ahmed Sbai
  • 10,695
  • 9
  • 19
  • 38
  • I tried this approach. But it has a problem. The state changes only on the next render. The data is fetched in the same cycle. But the state is updated only in the next render cycle. – Amrut Wali Jul 13 '23 at 14:58
  • Maybe because you update based on state1 and state2 values, you dont do that – Ahmed Sbai Jul 13 '23 at 15:05
  • You have to make your logic based on e.target.value instead dont expect the stat value to changes immediately when you set it – Ahmed Sbai Jul 13 '23 at 15:07
  • Hey thanks for the inspiration. I found a Solution which is pretty similar to what you have proposed. I have just made use of a useRef to handle both the inputs. I have isolated the value property from the state and kept only currency and url in the state. Now in the handleChange function, the inputRef's get changed. `input1Ref.current.value = input2Ref.current.value * rate;` – Amrut Wali Jul 13 '23 at 16:55