Dawn17
Dawn17

Reputation: 8297

React Hooks useEffect won't use the correct state

<Button
    onClick={() => {
        console.log('addedNodes', addedNodes)
        let credentials = //something...
        let nodes = [...addedNodes]
        console.log(addedNodes)
        setCurrentForm(
            {
                ...currentForm,
                credentials: credentials,
                nodes: [...addedNodes],
            }
        )
    }}
</Button>

I have a button that updates the currentForm state using another state addedNodes. Whenever the currentForm gets updated, I console.log the currentForm using useEffect.

useEffect(() => {
        console.log('currentForm ,,,,,, ', currentForm)
        console.log('addedNodes ,,,,,, ', addedNodes)
    }, [currentForm]);

This prints out the CORRECT updated state.

However, when I try to add an API request using that state, it goes back to the state before it got updated.

For example, when I update my useEffect to

useEffect(() => {
        console.log('currentForm,,,,,, ', currentForm)
        console.log('addedNodes ,,,,,, ', addedNodes)
        console.log('RUNNING POST')
        setLoadingStatus('loading')

        let body = {
            form: currentForm,
        }

        intializeForms()
        let options = {
            headers: header,
            method: 'post',
            mode: 'cors',
            body: JSON.stringify(body),
        }
        console.log('options.body', options.body)

        const urls = ['...'];
        const fetchJson = url => fetch(url, options).then(res => res.json());
        Promise.all(urls.map(fetchJson))
            .then(([result]) => {
               ...
            })
            .catch(err => {
                setLoadingStatus('none')
                console.log(err)
            });

    }, [currentForm]);

The console.log('options.body', options.body) prints out the old currentForm.

This is very weird to be because console.log(currentForm) prints the expected state, but when I actually use it for an API call, it goes back to the original form.

I assume that it is because this useEffect gets called everytime the state gets updated, but not really sure.

Any help, please?

Upvotes: 0

Views: 86

Answers (2)

xadm
xadm

Reputation: 8418

Problematic code fragment

    let body = {
        form: currentForm,
    }
    intializeForms()
    // later bad body.form content

form gets a reference to currentFrom object then currentFrom is overwritten in intializeForms() ... this way JSON.stringify(body) operates on bad data.

Why Kaca992's solution didn't worked?

let body = {
    form: {...currentForm},
  }

It should create a fresh object from currentForm' element/properties.
Probably it worked for some part of currentForm, f.e. for nodes as they was properly (in immutable way - by new instance) assigned/passed:

 nodes: [...addedNodes],

Probably other currentForm elements are a copies of always the same object references, mutated on changes, not replaced with new instances.

Solution:

In this case it's enough to call intializeForms() just after currentForm "consuming" (stringify) - let options = block.

Other good place for form reset (intializeForms() call) can be Promise.all(... resolving function (.then part).

Upvotes: 1

Kaca992
Kaca992

Reputation: 2267

What does initializeForms do? If currentForms is a reference type maybe you reset the value? Try setting the body like this:

    let body = {
        form: {...currentForm},
    }

Upvotes: 0

Related Questions