1

I have the useEffect function

useEffect(() => {
  let branchName = getBranchName();
  setBranchName(branchName);
  refreshBranchStatus()
}, []);

I want to call the refreshBranchStatus()function once, after setting the state of branchName

I tried write await before setBranchName(branchName); but it didn't work.
I tried write a second function

  useEffect(async () => {
    if (isFirstRender.current) {
      isFirstRender.current = false;
      await refreshBranchStatus();
    }
  }, [branchName]);

but it is called even when branchName has not changed(after page load, not after setBranchName(branchName);)

How can I get the correct behavior?
thanks in advance

guest381
  • 155
  • 1
  • 2
  • 12

2 Answers2

4

A key question to first ask yourself is:

Should refreshBranchStatus be re-invoked if branchName changes?

If it depends on the value of branchName then in the majority of cases, yes, it probably should be re-invoked. But a comment thread on this answer seems to be implying that this is an obscure case where it depends on the value but shouldn't be re-invoked if that value changes.

Based on the answer to the above, there are two approaches:


Option 1: Re-invoke when state changes

Create a second effect. The first is triggered only once when the component is first loaded:

useEffect(() => {
  let newBranchName = getBranchName();
  setBranchName(newBranchName);
}, []);

The other is triggered when the branchName state value changes:

useEffect(() => {
  refreshBranchStatus();
}, [branchName]);

Note: This should also run on the initial load because the branchName state value "changed" (or "was set to") its initial state. So I'd expect this effect to be executed twice. Once on the initial state of branchName and once on the updated state of branchName. (And again on any subsequent updates of that state.)


Option 2: Remove the dependency on state

If you truly only want to invoke refreshBranchStatus once, and only once, when the component first loads and it needs the value of branchName to execute then you should pass that value to the function. Something like this:

useEffect(() => {
  let newBranchName = getBranchName();
  setBranchName(newBranchName);
  refreshBranchStatus(newBranchName);
}, []);

In this scenario refreshBranchStatus isn't internally checking the branchName state but is more of a "pure" function which requires that it be provided with the value it needs. Since you have that value when you invoke the function, just pass it to the function.

David
  • 208,112
  • 36
  • 198
  • 279
  • I tried write second useEffect, but the second effect is fired right after the page is loaded, and not after the state branchName changed by setBranchName function. And I need call refreshBranchStatus(); only once – guest381 Feb 16 '22 at 17:31
  • @guest381: I'd expect it to be executed both times. Once for the initial state of `branchName` and then again for the updated state of `branchName`. Is that not the case? If it's failing in some way because the initial state of `branchName` is invalid for that operation then that's a separate concern. Can you clarify what specifically is failing? – David Feb 16 '22 at 17:33
  • it doesn't work because there is condition `if (isFirstRender.current) ` isFirstRender default true. Condition is necessary in order not to call refreshBranchStatus every time the branch is changed, because the UI has a button for this – guest381 Feb 16 '22 at 17:42
  • @guest381: `isFirstRender` implies a bit of a hack to me. It sounds like you're trying to fight React's re-rendering instead of using it as intended. When *should* `refreshBranchStatus` execute? If it depends on the value of `branchName` then logically it should execute any time `branchName` changes, shouldn't it? If `branchName` can be changed, why don't you want `refreshBranchStatus` to re-execute when `branchName` changes? – David Feb 16 '22 at 17:46
  • because refreshBranchStatus calls http request to the backend server which takes a long time, and second because there is a button "Refresh" in the UI which call refreshBranchStatus too – guest381 Feb 16 '22 at 17:51
  • @guest381: So then `refreshBranchStatus` *shouldn't* be invoked when `branchName` changes? Even though it depends on `branchName`? – David Feb 16 '22 at 17:54
  • @guest381: I've updated the answer with another option. If `refreshBranchStatus` shouldn't depend on the state of `branchName` then don't have it internally read that state value. Instead, pass the value to it when it's invoked. Then you can keep it in the same `useEffect` for when the component first loads. – David Feb 16 '22 at 18:04
  • `refreshBranchStatus` should be called once, after loading the page and after getting the value from `getBranchName` and setting the state `branchName` – guest381 Feb 16 '22 at 18:04
  • Thanks for you help @David – guest381 Feb 16 '22 at 18:07
0

Every usEffect will be called on the initial render of the component, so if you're waiting for a value to be set, you need to check for it in the effect body.

I think what you're asking for could be achieved like this:

useEffect(() => {
  let branchName = getBranchName();
  setBranchName(branchName);
}, []);

useEffect(() => {
  if (branchName) refreshBranchStatus();
}, [branchName]);
Zorac
  • 339
  • 4
  • 7