0

I am looking for some help in delaying the display of some calculated data until the state that it relies on is set in a parent component. In the below-simplified example, App has a value set to 0, then some calculations are run and the state is updated.

The problem is that when Calculate is clicked, the calculation which updates the state of the initial val hasn't yet happened, and the final value derived from calcOverUnderPayment is therefore incorrect.

It only displays the correct value on all subsequent clicks.

What could I do to fix this?

Thanks so much in advance.

function App() {
  const [val, setVal] = useState(0)

  const calculate = () => {
    // code to run some calculations which I then set as state
    setVal(100)
  }

  return (
    <>
      <button onClick={() => calculate()}>Calculate</button>
      <Outcome
        val={val}
      />
    </>
  )
}

function Outcome(val) {
  const calcOverUnderPayment = (value: number, type: string) => {
    if (type === 'under') {
      return (value > 0) ? value : 0
    } else {
      return (value < 0) ? value : 0
    }
  }

  return (
    <>
      Final Val is {calcOverUnderPayment(val)}
    </>
  )
}
SkrewEverything
  • 2,393
  • 1
  • 19
  • 50
Le Moi
  • 975
  • 2
  • 15
  • 41

2 Answers2

1

I have gone through comments of yours on the other answer. You can use another state variable and useEffect hook in your Outcome component.

function App() {
  // If it is possible, try to change the initial value to null so that the Outcome component can figure out when did the value change.
  const [val, setVal] = useState(null)

  const calculate = () => {
    // code to run some calculations which I then set as state
    setVal(100)
  }

  return (
    <>
      <button onClick={() => calculate()}>Calculate</button>
      <Outcome
        val={val}
      />
    </>
  )
}

function Outcome({ val }) {
  // Use this state to display initial value and update this state instead of calling the function directly in the return
  const [valueToDisplay, setValueToDisplay] = useState('YOUR_INITIAL_VALUE_TO_DISPLAY');

  const calcOverUnderPayment = (value: number, type: string) => {
    if (type === 'under') {
      return (value > 0) ? value : 0
    } else {
      return (value < 0) ? value : 0
    }
  }

 // use useEffect hook to run your function whenever val prop is changed 
  useEffect(() => {
    // If the val prop is null, then it is evident that it is rendering for the first time. 
    // So, don't run the calcOverUnderPayment() method. This is why "null" is important to provide as your initial state for "val" in App component
    if(val !== null) {
      const value = calcOverUnderPayment();
      // set the state variable here to display
      setValueToDisplay(value);
    }
  },[val]) // Give the val prop as the dependency. This tells useEffect to run whenever val prop is changed.

  return (
    <>
      {/* Instead of calling your function here, use the state variable*/}
      Final Val is {valueToDisplay}
    </>
  )
}

This should work as you intended. Let me know if you encounter any problem or didn't understand it.

SkrewEverything
  • 2,393
  • 1
  • 19
  • 50
0

As one of the options you could just use conditional rendering, and render your child component, only when state isn't default, in your case something like this would do:

  return( //your components here 
        {val != 0 && <Outcome val={val} />}
       //rest of your component
  )
Nikita Chayka
  • 2,057
  • 8
  • 16
  • This is a good option but won't work in this case as I need to have the component displayed already, also, that specific val could be zero and still have some calculations run on other parts to produce a result that would need to be displayed. – Le Moi Nov 04 '20 at 15:27
  • well this becomes unclear to me on what you need exactly, what kind of calculations that is? Maybe you just do another useState(false) -> that will flag you if your calculations are on the way or not, and based on that you could do whatever you need to do (which became unclear to me by this point) – Nikita Chayka Nov 04 '20 at 15:31
  • useState(false) is an option, but I am setting three different values using setState. How can I determine when all three of those values' state has updated to then change the boolean of the false flag? I will update my question to be clearer of my requirements – Le Moi Nov 04 '20 at 15:33
  • It's interesting, yeah, would need to see your problem exactly, but you could do your 3 values into one state object and then use something like this - https://stackoverflow.com/questions/53446020/how-to-compare-oldvalues-and-newvalues-on-react-hooks-useeffect – Nikita Chayka Nov 04 '20 at 21:57