Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

loading: false, error: undefined, value: undefined when using useDocument #17

Closed
jameshartig opened this issue Apr 21, 2019 · 6 comments · Fixed by #55
Closed

loading: false, error: undefined, value: undefined when using useDocument #17

jameshartig opened this issue Apr 21, 2019 · 6 comments · Fixed by #55

Comments

@jameshartig
Copy link

jameshartig commented Apr 21, 2019

Thanks to #10, we are now able to do:

  const { initialising, user } = useAuthState(auth);
  const { loading, error, value } = useDocumentOnce(
    user ? db.doc(`users/${user.email}`) : null
  );

The problem is that if the user exists then a document reference is sent but useDocumentOnce returns { loading: false, error: undefined, value: undefined }. I was under the assumption that once loading is false, the value should be the DocumentSnapshot. It looks like this was caused by 01e8616 because calling setValue(undefined) ends up setting the loading value to false.

I'm not sure if the best fix is to change the useLoadingValue's reducer to check the value before setting loading to false or if instead it shouldn't call setValue(undefined) if the value is already undefined.

@chrisbianca
Copy link
Contributor

@fastest963 I'm struggling to reproduce this.

setValue(undefined) will only ever be called when there is no document reference sent to useDocumentOnce.

When a document reference is specified then the state is reset and loading is set to true until a response is received from Firestore, at which point the DocumentSnapshot will be set as the value.

My test case for this is as follows, which tests by cycling through two different document references and a null reference:

const FirestoreDocument = () => {
    const [ path, setPath ] = useState('hooks/nBShXiRGFAhuiPfBaGpt');
    const ref = path ? firebase.firestore().doc(path) : null;
    const { error, loading, value } = useDocumentOnce(ref);

    const togglePath = () => {
        setPath(prevState => prevState === 'hooks/nBShXiRGFAhuiPfBaGpt' ? 'hooks2/32ofSNdNtGL487vpSpZZ' : prevState === 'hooks2/32ofSNdNtGL487vpSpZZ' ? null : 'hooks/nBShXiRGFAhuiPfBaGpt');
    }

    return (
        <div>
            <p>
                {error && <strong>Document Error: {error}</strong>}
                {loading && <span>Document: Loading...</span>}
                {!loading && (
                    <span>
                        Document: {value ? JSON.stringify(value.data()) : 'undefined'}
                    </span>
                )}
            </p>
            <p>
                <button onClick={togglePath}>Toggle Path</button>
            </p>
        </div>
    );
}

@jameshartig
Copy link
Author

@chrisbianca interesting. I assume this is some sort of race condition then. If I add a breakpoint and am able to step through the stack, is there anything that would help you identify how it's happening?

@chrisbianca
Copy link
Contributor

So I've just been doing some digging, and it looks like there is an extra render cycle which is causing what you're seeing. In my example above, I've added some logging, which produces the following when switching from null to a proper doc reference:

// Loaded state with `null` document reference
Time: 1557241681875; Error: undefined; Loading: false; Value: undefined
 // Click button to switch to document
Toggle: hooks/nBShXiRGFAhuiPfBaGpt
// Previous state is rendered again once
Time: 1557241823511; Error: undefined; Loading: false; Value: undefined 
// New loading state is rendered
Time: 1557241823514; Error: undefined; Loading: true; Value: undefined
// Document has loaded
Time: 1557241823877; Error: undefined; Loading: false; Value: [object Object]

As I understand it, the reason for this is that toggling the button calls setState which triggers a render. It is only on this render that the firebase hook can update the document reference, hence why there is a repeat render of the "old" state.

In essence this shouldn't cause an issue, but it's not ideal. I'll do some reading / digging to see if there's a better way to workaround this sort of issue when chaining hooks together.

@Nickman87
Copy link
Contributor

@chrisbianca I might have found a solution for this issue

#55

I don't think it is correct to call setValue in the hooks when no value is present, a reset sounds more logical and solves the loading state switching to loaded.

@royletron
Copy link

royletron commented Nov 10, 2020

Hey all. I'd like to reopen this issue as it is still causing some grief for me. The basic flow is:

const {userId} = useContext(UserContext);
const [profile, loading, error] = useDocumentData(userId ? firestore().collection('profiles').doc(userId) : null);
console.log(userId, profile, loading, error);

Assuming the userId starts off as undefined as it is loaded in context, you get the following console.log:

1. undefined, undefined, false, undefined
2. 'fxxxxxx01', undefined, false, undefined
3. 'fxxxxxx01', undefined, true, undefined
4. 'fxxxxxx01', {documentData}, false, undefined

The problem is number 2. causes issues downstream, because I might want to take a case where the profile hasn't yet been setup by the user, but it's impossible to check between that state (where the console.log would look like 'fxxxxxx01', undefined, false, undefined) and number 2. which will happen everytime I check - I've actually resorted to a debounce hook 😱

@tsbajwa
Copy link

tsbajwa commented Nov 18, 2020

I also seem to be getting the same issue described by @royletron

uid is initially undefined, and a value is then loaded/set via context

const data = (uid: string) => { const [value, loading, error] = useDocumentData( uid ? db().collection(uid).doc("doc") : null );

My current solution is the following
const data = (uid: string) => { const [value, loading, error] = useDocumentData( uid ? db().collection(uid).doc("doc") : db().collection("FAKE_VALUE_NOT_EXISTING_IN_DB").doc("doc") );

Passing any reference seems to set the loading state correctly. Not sure if i am potentially causing other issues but seems to work for now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants