Why when using setState(...) is the console.log null?

2k Views Asked by At

Why when I console.log(data) is does it log the data but when I try and log the item set by set state its null? I don't understand is it because it doesn't have time to update before console logging?

  const [currentUser, setCurrentUser] = useState(null);
        const [pending, setpending] = useState(true);
        const [userData, setuserData] = useState({});

        useEffect(() => {
            Authentication.auth().onAuthStateChanged((user) => {
                setCurrentUser(user)
                setpending(false)
                localStorage.setItem('user', user.uid);
                console.log(localStorage.getItem('user'));
            });

            getData()

        }, []);

        const getData = () => {
            Authentication.firestore().collection('Health_data')
                .doc(localStorage.getItem('user'))
                .get()
                .then(doc => {
                    const data = doc.data();
                    localStorage.setItem('user_data', JSON.stringify(data));
                    setuserData(data)
                    console.log(data)
                }).catch(function (error) {
                    console.error("Error reading health", error);
                });
                console.log(userData)
        }
3

There are 3 best solutions below

7
On

I'm going to assume you're confused by the output of console.log(userData); at the end of getData. There are two reasons why it doesn't show the updated user, either of which would be enough on its own. :-) They are:

  1. The console.log(userData) happens before the setuserData call, because promise callbacks are always asynchronous.

  2. userData is a constant within the Example function call, its value will not change. When the state is changed, Example will get called again, and that new userData constant will have the updated user in it. (Note that it doesn't matter whether it's declared with let or const, it's effectively a constant either way.) Each of those constants exists in an execution context that's tied to the specific call to Example the constant was created in. More in Dan Abramov's article A Complete Guide to useEffect (which is about much more than useEffect).

Here's a demonstration:

const { useState } = React;

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

let executionContext = 0;
const Example = () => {
    const contextNumber = ++executionContext;
    const [value, setValue] = useState(0);

    const log = msg => console.log(`[${contextNumber}]: ${msg}`);

    log(`Example called, value = ${value}`);
    
    const onClick = () => {
        log(`onClick`);
        delay(800)
        .then(() => {
            const newValue = Math.floor(Math.random() * 10000);
            log(`calling setValue(${newValue})...`);
            setValue(newValue);
            log(`value is still ${value} (reason #2)`);
        });
        log(`value is still ${value} (reason #1)`);
    };
    
    return (
        <div>
            {value}
            <div>
                <input type="button" onClick={onClick} value="Click Me" />
            </div>
        </div>
    );
}


ReactDOM.render(<Example/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

4
On

If you are talking about this code:

Authentication.firestore().collection('Health_data')
                .doc(localStorage.getItem('user'))
                .get()
                .then(doc => {
                    const data = doc.data();
                    localStorage.setItem('user_data', JSON.stringify(data));
                    setuserData(data)
                    console.log(data)
                }).catch(function (error) {
                    console.error("Error reading health", error);
                });
                console.log(userData)

The problem is

Authentication.firestore().collection('Health_data')
                .doc(localStorage.getItem('user'))
                .get()

Is async execution, so console.log(userData), sometimes will execute before the Authentication call and the value will be null.

So you need to ensure that the Authentication call has finished

0
On
if(userData !== null) {
    console.log(userData)
}

this code seems to work for me but I'm not sure how I can use this to access the information on other pages the same way?