-2

Consider the following code

const App = () => {
    const [errorMsgs, setErrorMsgs] = useState([])
    const onButtonPressed = () => {
        fetchErrors(setErrorMsgs)
        if (!errorMsgs.length) {
            // Procced and do somethings
        }
    }
}
function fetchErrors(setErrorMsgs) {
// if some condition is true:
setErrorMsgs(errorMsgs => [...errorMsgs, "some error"])
// if some other condition is true:
setErrorMsgs(errorMsgs => [...errorMsgs, "some other error"])
}

As far as I understand, when using setErrorMsgs, it doesn't immediately update the state, and this cannot be solved by using await or .then() because setErrorMsgs doesn't return a promise, therefore, the condition will never be true in this sample. So what should I do so the condition runs after the state updates? (I don't want the condition to run every time the errorMsgs changes.)

Edit: To clarify:
The problem, when I call fetchErrors function, it checks some conditions and might append one, or more errors to errorMsgs. When I check to see if errorMsgs is an empty array, the condition will be true even if the fetchErrors adds anything to the array.

Parsa
  • 141
  • 8
  • 1
    Given that the new value you want to test against is always true, do you need the condition at all?! – jonrsharpe Aug 05 '22 at 21:31
  • Does this answer your question? [Can I execute a function after setState is finished updating?](https://stackoverflow.com/questions/34687091/can-i-execute-a-function-after-setstate-is-finished-updating) – Cyrus Aug 05 '22 at 21:32
  • @jonrsharpe I don't understand what you mean by "is always true"? the initial value is false. And this is just a sample, simplified code which I wrote which demonstrates my original problem. – Parsa Aug 05 '22 at 21:33
  • @Cyrus I don't think so, my question is about `useState` but the suggested question is about `setState`. – Parsa Aug 05 '22 at 21:36
  • why can't you use `useEffect`? `useEffect(() => { /* do your thing */ }, [state]}` should work, `state` must be defined at the top level of the component else you're breaking the Rules of Hooks. (And it is correctly placed here.) – Robin Zigmond Aug 05 '22 at 21:37
  • @Parsa setState is a part of useState – Cyrus Aug 05 '22 at 21:38
  • The _new_ value is always true. Maybe this example isn't representative, but then: give one that is. – jonrsharpe Aug 05 '22 at 21:39
  • @RobinZigmond In that case, the `useEffect` runs every time the `state` changes, in my problem, the state might change many times before running the last `setState`. I want the condition to run after the last change. – Parsa Aug 05 '22 at 21:39
  • How do you know it's the "last" change? What's the real context here? – jonrsharpe Aug 05 '22 at 21:40
  • @jonrsharpe Sorry if the problem was not clear, I edited the question to clarify. I hope it's more clear now. – Parsa Aug 05 '22 at 21:46
  • 1
    I'm still confused what you're trying to do and what the problem is. If you only want to run some code when `state` changes and it's also conditional on the value of `state`, just put an appropriate `if` statement inside the `useEffect` function. But you can also do this inside the event handler, schematically it will be something like `const newState = someNewValueCalculatedHoweverYouWant; setState(newState); if (newState === whatever) {...}` – Robin Zigmond Aug 05 '22 at 21:48
  • Not really: "some condition"... _what_ condition? What's the actual thing you're trying to do here? And why are you passing the setter to a separate function, so the component loses control of its own state? – jonrsharpe Aug 05 '22 at 21:49
  • @jonrsharpe Again, I edited the question. Is the problem more understandable now? – Parsa Aug 05 '22 at 21:59
  • It still doesn't make sense to me, vague descriptions aren't as much use as code (it's still "some condition"). If you want all of the errors before you do anything why wouldn't you just `fetchErrors().then((newErrors) => ...)`? That means the component keeps control of its state too. – jonrsharpe Aug 05 '22 at 22:04
  • @jonrsharpe Even if I do that, again, I should use `setErrorMsgs` to update `ErrorMsg`, the only difference would be that I'm doing it in the first component. About the "some condition" part, In the original code, I'm also passing some other variables(which are inputs from the user) to `fetchErrors()`, the function is supposed to add some errors to `errorMsgs` if certain conditions were true about the user's inputs. `errorMsgs` is also shown to the user at the same time so they can see the errors. – Parsa Aug 05 '22 at 22:24

2 Answers2

0

You can't assume that when you change state the variable had it's value changed, but react have a correct way to handle it, by using useEffect, so

    const [state, setState] = useState(false)

    const onButtonPressed = () => {
        setState(true)
        
    }
    useEffect(() => {
       // here you can do some stuff
    },[state])
Aloiso Junior
  • 417
  • 3
  • 10
  • In this case, the `useEffect` runs every time the `state` changes, in my problem, the state might change many times before running the last `setState`. I want the condition to run after the last change. – Parsa Aug 05 '22 at 21:39
  • Okay inside your onButtonPressed, if your state have a desired value you not call setState, how about? – Aloiso Junior Aug 05 '22 at 21:41
0

You can redesign your fetchErrors function, to return the value from the condition, instead of running setter function, then you can do what @RobinZigmond has suggested:

const newErrors = fetchErrors(); 
setState(newErrors); 
if (!newErrors.length) {...}

this way you know for sure what your next state will be like.

Mod3rnx
  • 828
  • 1
  • 10
  • 21