2

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)
        }
lukeet
  • 461
  • 1
  • 4
  • 22

3 Answers3

4

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>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • the first point is probably the reason because in the react plugin I can see userData changing, but can't seem to get it to print in any of my files – lukeet Apr 02 '20 at 12:39
  • I can see this in the react component plugin https://imgur.com/undefined – lukeet Apr 02 '20 at 12:41
  • @lukeet - As I said, it's both. I've beefed up the explanation of #2 a bit and added a demonstration. (BTW, that link doesn't work.) – T.J. Crowder Apr 02 '20 at 12:49
  • https://imgur.com/7cl1gJ7 does this work as you can see the data with userData does change and is updated I don't quite understand what you're trying to say? – lukeet Apr 02 '20 at 13:02
  • @lukeet - Did you read the updated answer and look at the demonstration? The `userData` constant within a given execution context can't change. But your function is called again, which creates a new execution context, with a new constant in it. But the *old* one may still be referenced by closures within the original execution context. That's why two reasons. :-) – T.J. Crowder Apr 02 '20 at 13:06
  • 1
    Trying to wrap my head around that hahaha thanks for the update and the code snippet – lukeet Apr 02 '20 at 13:11
  • @lukeet - You're welcome! Hope it helps. Dan Abramov's article is really useful for understanding this stuff. :-) – T.J. Crowder Apr 02 '20 at 13:16
1

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

  • This sounds like a good solution how do I do this? because in the components react extension I see the string i want to reproduce https://imgur.com/undefined – lukeet Apr 02 '20 at 12:42
  • Hey the image is 404 :( Maybe isn't a good idea print `userData`, I suppose you want to show things based on this, try to use in the jsx, like this: `return
    { userData &&
    here you will able to user the userData
    }
    `
    – Gonzalo Gras Cantou Apr 02 '20 at 12:48
  • https://imgur.com/a/voixpQJ is that better just wanted to show that the console log data was being printed. That's not a bad idea of printing a div. – lukeet Apr 02 '20 at 12:59
  • 1
    Yes, you could do something like this: `{ userData &&
    user age {userData.age}
    }`
    – Gonzalo Gras Cantou Apr 02 '20 at 13:05
0
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?

lukeet
  • 461
  • 1
  • 4
  • 22