Git Product home page Git Product logo

react-firebase-hooks's People

Contributors

andipaetzold avatar attaradev avatar caleb-harrelson avatar cargallo avatar chrisbianca avatar dependabot[bot] avatar dohomi avatar dsvgit avatar dylanwatsonsoftware avatar exkazuu avatar kqito avatar lemol avatar lorstenoplo avatar lucacasonato avatar matamatanot avatar mauriceackel avatar michaeltroya avatar mparsakia avatar neilor avatar nickman87 avatar prashoon123 avatar pzmudzinski avatar robertsasak avatar sdemjanenko avatar shaunlwm avatar simonelnahas avatar snailed avatar tornewuff avatar valmassoi avatar ypresto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-firebase-hooks's Issues

Dependent hooks

Seems like a common flow I see for defining admin users is to have an admin collection, and then you can check the authenticated user against that collection to make a determination.

Since hooks can't show conditionally, the syntax for doing this seems to be along the lines of:

const { initializing, user } = useAuthState(auth);
const { loading, error, value } = useDocument(db.doc(`admins/${ user && user.email }`));
...
const isAdmin = !!value.exists;

In this case, we begin by querying via useDocument for a document we know won't work (admins/), but it of course works fine when user.email is available.

Is there a better way of doing this with hooks and firebase?

Refresh data for useDocumentDataOnce

I couldn't find anything about refreshing data for a document that is loaded using useDocumentDataOnce. My app will have a refresh button or a pull-to-refresh gesture.

I tried using a dummy local state to force the re-render but it doesn't work since the document refs are the same across renders.

const [refreshCount, setRefreshCount] = useState(0);
const refresh = () => setRefreshCount(refreshCount + 1);

Is this possible to implement this behaviour?

Add or update to firebase

Unless I'm missing something, besides listening to the firebase DB, how can we send or update data to it with some custom hook?

Request: add generics to mirror firestore and improve type safety

react-firebase-hooks rocks, btw -- thanks!

slight (trivial) suggestion:

// current
export const useDocument = (
  docRef?: firestore.DocumentReference | null,

// proposed
export const useDocument<T = firestore.DocumentData> = (
  docRef?: firestore.DocumentReference<T> | null,

this would also apply to useCollection, etc.

This would be helpful because firestore does indeed preserve the type all the way to the subsequent snapshot data() value.

As a side-benefit, a typed ref would improve the useDocumentData API allowing you to deduce the returned data type without the explicit generic:

// current
const [data, loading, error] = useDocumentData<MyData>(ref);

// becomes (optional, if your ref happens to be typed)
const [data, loading, error] = useDocumentData(myDataRef); // myDataRef is DocumentReference<MyData>

ps: best practice would still be to validate the payload with yup etc. but this would go a long way!

initialising and error not working on Auth Hook

Just tried the example you provided

import { useAuthState } from 'react-firebase-hooks/auth';

const CurrentUser = () => {
  const [user, initialising, error] = useAuthState(firebase.auth());
  const login = () => {
    firebase.auth().signInWithEmailAndPassword('[email protected]', 'password');
  };
  const logout = () => {
    firebase.auth().signOut();
  };

  if (initialising) {
    return (
      <div>
        <p>Initialising User...</p>
      </div>
    );
  }
  if (error) {
    return (
      <div>
        <p>Error: {error}</p>
      </div>
    );
  }
  if (user) {
    return (
      <div>
        <p>Current User: {user.email}</p>
        <button onClick={logout}>Log out</button>
      </div>
    );
  }
  return <button onClick={login}>Log in</button>;
};

Same result. Uncaught error. Using v2.1.0

How to use multiple hooks that depend on each other?

Example:

const [authUser, authLoading, authError] = useAuthState(firebase.auth());
const [user, userLoading, userError] = useDocument(firebase.firestore().doc(`users/${authUser.uid}`));

I can't put useDocument in a if (authUser) because React breaks with: Rendered more hooks than during the previous render.

Auth user email Verification changes doesn't triggers re-render

Thanks for this library I guess this issue is related with Firebase/auth itself and not the hook but this is the journey:

Steps to reproduce

  1. Not register user go to a registration form in Tab1
  2. the Registration form shows a user.emailVerified: false on the top
  3. The user register using the form
  4. A verification email is sent to the user
  5. The form now shows a message pending verify email
  6. The user goes to the email and clicks in the verification link opening Tab2

Current result

  • Returning to the Tab1 the message user.emailVerified: false remains
  • if in the console I access the user entity user.emailVerified -> true

Expected result

user.emailVerified changes triggers a component re-render

  • Returning to the Tab1 the message is user.emailVerified: true

useDownloadURL not working with reference

I'm trying to use the useDownloadURL method for a file reference in my firebase storage. Getting an array error:

image

Here is my code:

const [
    value,
    loading,
    error,
  ] = useDownloadURL(
    firebase.storage().ref(`avatars/eCP7rPbQbMOABgklojtQDPxFE6A3.jpeg`)
  );

Optional parameter for useLayoutEffect passed into useAuthState

Are there are any plans to provide an option to use useLayoutEffect instead of useLayout?

If on application load useAuthState is leveraged to determine whether to render a sign in screen or user dashboard, then the sign in screen is painted before the component receives the updated state. That causes a quick flash of the sign in screen.

This causes PublicRoutes to flash:

const Routes = () => {
  const { user } = useAuthState()
  return (
    user
      ? <AuthenticatedRoutes /> // <-- user dashboard is here
      : <PublicRoutes /> // <-- sign in screen is here
  )
}

Here is a proposed change to useAuthState.ts:

import { auth, User } from 'firebase';
import { useEffect, useLayoutEffect } from 'react';
import useLoadingValue from '../util/useLoadingValue';

export type AuthStateHook = {
  user?: firebase.User;
  initialising: boolean;
};

export default (auth: auth.Auth, delayPaint: boolean): AuthStateHook => {
  const { loading, setValue, value } = useLoadingValue<User>(auth.currentUser);
  const effect = delayPaint ? useEffect : useEffect;

  effect(
    () => {
      const listener = auth.onAuthStateChanged(setValue);
      return () => {
        listener();
      };
    },
    [auth]
  );

  return {
    initialising: loading,
    user: value,
  };
};

loading is false when ref if changed.

Version: 2.0.0

After changing the ref in useDocumentData I first get loading: false, value: undefined.
It seems that the isEqualRef is called afterward.

When the ref change, loading should immediately set to true.

Firestore: How to fetch data to init state?

Consider this example:
Categories are stored in firestore. I need to display checkboxes to select these categories, so I need to store the checked state. My approach results in an infinite loop:
Inside my component function:

const [categories] = useCollectionDataOnce(
		firestore.collection('categories'),
		{idField: 'id'},
	);

	const [categoriesBoxesValues, setCategoriesBoxesValues] = useState([]);

	useEffect(() => {
		if (!categories) {
			return;
		}

		const boxes = categories
		.map(c => ({
			isChecked: false,
			...c,
		}))

		// fixme: causes infinite loop
		// setCategoriesBoxesValues(boxes);
	}, [categories]);

Seems to be a common use case, namely to enhance a value fetched from firestore. How to solve this issue?

Getting delayed/no response on mobile

Hi!

Use this lib for a project with mainly mobile users. In development on simulated iOS and android updates to the document I'm watching are seen immediately but on real devices there is a significant lag, but is solved by a refresh of the page.

Any ideas what I can do to solve?

How do you get the data from the database

I'm trying to figure out how to use this.

My best attempt is copied below, although it generates an error that says:

ReferenceError: Cannot access 'snapshot' before initialization

[const GeneralTest = props => {
  const { register, handleSubmit, setValue, errors, reset } = useForm();
  const { action } = useStateMachine(updateAction);
  const onSubit = data => {
    action(data);
    props.history.push("./ProposalMethod");
  };


  const [snapshot, loading, error] = useDocumentOnce(
    firebase.firestore().collection('abs_for_codes'),
    snapshot.push({
      value: snapshot.data().title.replace(/( )/g, ''),
      label: snapshot.data().title + ' - ABS ' + snapshot.id
    }),
  );

  const [valuesField, setField ] = useState({
    selectedOptionField: []
  });

  const handleMultiChangeField = selectedOption => {
    setValue("field", selectedOption);
    setField({ selectedOption });
  };

  const handleMultiChangeField = selectedOption => {
    setValue("field", selectedOption);
    setField({ selectedOption });
  };

  useEffect(() => {
    register({name: "field"});
  }, []);

]


Before hooks, I was able to use componentDidMount like so:

[class Form extends React.Component {
    state = {
      options: [],
    }

    async componentDidMount() {
        // const fsDB = firebase.firestore(); // Don't worry about this line if it comes from your config.
        let options = [];
        await fsDB.collection("abs_for_codes").get().then(function (querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, ' => ', doc.data());
            options.push({
                value: doc.data().title.replace(/( )/g, ''),
                label: doc.data().title + ' - ABS ' + doc.id
            });
            });
        });
        this.setState({
            options
        });
    }]

I'm looking to figure out how to use this tool to extract data from the database and then use it to populate a select menu, like so:

<Select 
            className="reactSelect"
            name="field"
            placeholder="Select at least one"
            value={valuesField.selectedOption}
            options={snapshot}
            onChange={handleMultiChangeField}
            isMulti
            ref={register}
          />

Uncaught auth error

When I use useAuthState with signInWithPopup and close the popup without authenticating, the error state is not reached.

After a second these two errors appear in the console:

Uncaught: {code: "auth/popup-closed-by-user", message: "The popup has been closed by the user before finalizing the operation."}

Uncaught Error: The error you provided does not contain a stack trace.

I was expecting this error to cause useAuthState to reach the error state. Is this behavior expected?

The following code dropped into the CreateReactApp boilerplate will reproduce this error. In fact, all errors seem to cause the aforementioned result.

import React from 'react'

import * as firebase from 'firebase/app'
import 'firebase/auth'

import { useAuthState } from 'react-firebase-hooks/auth'

const firebaseConfig = {
  // copied from firebase console
}

firebase.initializeApp(firebaseConfig)

const provider = new firebase.auth.GoogleAuthProvider()

const login = () => {
  firebase.auth().signInWithPopup(provider)
}

export const logout = () => {
  firebase.auth().signOut()
}

export const App = () => {
  const [user, initialising, error] = useAuthState(
    firebase.auth(),
  )

  if (initialising) {
    return (
      <div>
        <p>Initialising User...</p>
      </div>
    )
  }
  if (error) {
    return (
      <div>
        <p>Error: {error}</p>
      </div>
    )
  }
  if (user) {
    return (
      <div>
        <p>Current User: {user.email}</p>
        <button onClick={logout}>Log out</button>
      </div>
    )
  }

  return <button onClick={login}>Log in</button>
}

My package versions:

{
  "firebase": "^6.6.2",
  "react": "^16.9.0",
  "react-firebase-hooks": "^2.1.0",
}

Thanks for the great library!

TypeError: v1.isEqual is not a function

I'm getting TypeError: v1.isEqual is not a function when I use the useDocument hook

const profileRef = firebase
    .firestore()
    .collection('users')
    .doc(user.uid)

const { error, loading, value } = useDocument(profileRef)
 "react": "16.8.3",
 "react-firebase-hooks": "^1.1.0",
 "react-native": "0.59.1",
 "react-native-firebase": "^5.2.3",

Any ideas?

Waiting for the result to get a second hook

I was wondering if there was a way to use this library to do async requests in multiple hooks. In my use case, I'm calling a document that has the id for another document and I need this id to get the other document.

const [selectionData, loadingS] = useDocumentData(firestore.doc(`selections/${selection}`), { idField: 'id' })
const [friendData, loadingF] = useDocumentData(firestore.doc(`friends/${selectionData.id}`), { idField: 'id' })

Before I was using a custom hook with an async function but I was wondering there was a way to do it with your library.

To give a bit of context on what I'm trying to achieve, this code do the trick for now:

function useOccasion() {
  const [occasionData, setOccasionData] = useState(null)
  const [friend, setFriend] = useState(null)
  let { occasion } = useParams()

  useEffect(() => {
    const unsubscribe = firestore.collection('occasions').doc(occasion)
      .onSnapshot(async eventData => {
        setOccasionData({id: eventData.id, ...eventData.data()})
        
        let friendData = await firestore.collection("friends").doc(eventData.data().friend).get();
        setFriend({id: friendData.id, ...friendData.data()});
      })
    return () => unsubscribe()
  }, [occasion])
  
  return [occasionData, friend]
}

So if there's a way to reproduce this with these hooks that would be amazing.

Always returns TypeError: Object(...) is not a function

Using the example provided I can't seem to get anything out of the store:

const FirestoreCollection = () => {
  const { error, loading, value } = useCollection(
    firebase.firestore().collection('hooks')
  );
  return (
    <div>
      <p>
        {error && <strong>Error: {error}</strong>}
        {loading && <span>Collection: Loading...</span>}
        {value && (
          <span>
            Collection:{' '}
            {value.docs.map(doc => (
              <React.Fragment key={doc.id}>
                {JSON.stringify(doc.data())},{' '}
              </React.Fragment>
            ))}
          </span>
        )}
      </p>
    </div>
  );
};

I just get the error Uncaught TypeError: Object(...) is not a function

How to handle Errors from Firestore

Hi,
I'm using

const query = firestore.collection('cms').doc('events').collection('cmsevents');
		const options =
		{
			idField: '_uid'
		}
		try {
			const [values, loading, fberror] = useCollectionDataOnce<IEvent>(query, options);
			console.log('values, loading, fberror', values, loading, fberror)

			// Production code
			return (
				<div>
					<Title>Your Events</Title>
					{error ? (
						<Alert variant="danger" onClose={() => setError(null)} dismissible>
							<Alert.Heading>Error</Alert.Heading>
							<p>{error}</p>
						</Alert>
					) : null}

					{fberror && <strong>Error: {fberror}</strong>}
					{loading && <span>Document: Loading...</span>}
					{values && <pre>Documents: {JSON.stringify(values, null, 2)}</pre>}

					<Button onClick={() => {
						history.push('events/_new');
					}
					}>Add</Button>


				</div>
			);
		} catch (error) {
			return (<pre>{JSON.stringify(error, null, 2)}</pre>)
		}

It works perfect.

But if i update my roles in firestore, i will get a exception
FirebaseError: Missing or insufficient permissions.

Getting this error is ok.
My expectation was, that this is also caught and add to error.
Unfortunately this is not the case and my side crashes.

Do you have any suggestions how to catch the firestore exception?

Many thanks!

Feature request for useCollection and useDocument - latest result timestamp

I really appreciate the work on this library.

I have one feature request. Is it reasonable to add a timestamp field to the return array?
So for example [value, loading, error, timestamp] = useCollection(...)

My use case is that I often use the last modified timestamp on a document to trip React caching in useEffect, useMemo, etc. However, this requires logic to look at the docs to see if any of them were modified in order to update the cache arguments to useEffect to do some processing. For most of my use cases, I could easily use the timestamp from the last time the FB onSnapshot event was triggered.

Request: useCollectionGroup and useCollectionGroupData

Hey!

Great lib! Wondering if it would be useful to add useCollectionGroup and useCollectionGroupData.

Use case: querying a single document using a collectionGroup query.
Example:

const SomeCollectionComponent = ({ someId }) => {
  // useDocument cannot be used since `useDocument` expects a `DocumentReference<DocumentData>` and my query returns a `Query<DocumentData>`
  [snapshots, error, loading] = useCollectionData(
    firebase
      .firestore()
      .collectionGroup('someCollection')
      .where('id', '==', someId)
  )

  // After rror and loading checks

  const document = snapshots[0]

  // do something with document
}

Proposed implementation:

useCollectionGroupData = (query) => {
  const [snapshots, loading, error] = useCollectionData(query)

  return [snapshots && snapshots[0], loading, error]
}

Lemme know if I can work on this!
Cheers

useAuthState initialising not working

Hi,

I've been trying the useAuthState hook and looks like the initialising variable is not working.

Here is the code:

export default function Login() {

  const { initialising, user } = useAuthState(firebaseAuth());
  const login = () => {
      firebaseAuth().signInWithEmailAndPassword('[email protected]', 'test123');
    };
  const logout = () => { firebaseAuth().signOut(); };
  
    if (initialising) {
      return (
        <div>
          <Spinner marginX="auto" marginY={50} />
        </div>
      )
    }
    if (user) {
      return (
        <div>
          <p>Current User: {user.email}</p>
          <Button onClick={logout}>Log out</Button>
        </div>
      );
    }
    return (
      <Button onClick={login}>Log in</Button>
    )
  }

The login/logout works fine, but I've confirmed that the initialising block is never reached.

Thanks in advance,

Geraldo

Proposal: apply transformations to returned object(s)

Still fleshing the idea out but wanted to note in case there was interest. It'd be handy for our use case to be able to apply transformations to returned object(s). Often times the data we have stored is not necessarily the data we want to display so we often wind up running the same transformations after fetch.

A few examples:

  • Our users always have a first_name and a last_name. Sometimes they also have a display_name. We always want the display_name to take precendence and run a transformation on retrieved data to do so.

  • We use Firebase timestamps which are really handy on the server and really not-so-handy on the client since they always need to be coerced with at least .toDate()

  • Currency formatting always winds up with something like .toLocaleString() or something. I can't even remember off the top of my head and would need to look into our codebase for how we typically format.

Suggested enhacnement

Provide a transformations property in options that all data is run through. A transformation could either be added to an object or overwrite a property of an incoming object.

For example, let's say my source object is a user:

{
  first_name: 'John',
  last_name: 'Smith',
  created_at: firebase.firestore.Timestamp
}

With transformations:

const [user, userLoading, userError] = useDocumentData(
  myCollectionRef.users('1234')
, {
  transformations: {
    display_name: (data) => {
      return data.display_name 
        ? data.display_name 
        : `${data.first_name} ${data.last_name}`
    },
    created_at: (data) => {
      return data.created_at.toDate().toDateString();
    }
  }
})

Expected output from console.log(user):

{
  first_name: 'John',
  last_name: 'Smith',
  display_name: 'John Smith',
  created_at: 'Wed Dec 18 2019',
}

I could see this coming in handy for us since we could store a handful of "schema" transformations and add to them with spread syntax. eg.

{
  transformations: {
    randomOneOffProp: (data) => data.field.toLowercase(),
    ...commonPeopleTransformsFromFile,
  }
}

Ideally the same could be applied to collection data

Typescript Errors

Hi.

I'm getting these:

node_modules/react-firebase-hooks/firestore/dist/firestore/useCollection.d.ts:2:29 - error TS2307: Cannot find module '../util'.

2 import { LoadingHook } from '../util';
                              ~~~~~~~~~

node_modules/react-firebase-hooks/firestore/dist/firestore/useCollectionOnce.d.ts:2:29 - error TS2307: Cannot find module '../util'.

2 import { LoadingHook } from '../util';
                              ~~~~~~~~~

node_modules/react-firebase-hooks/firestore/dist/firestore/useDocument.d.ts:2:29 - error TS2307: Cannot find module '../util'.

2 import { LoadingHook } from '../util';
                              ~~~~~~~~~

node_modules/react-firebase-hooks/firestore/dist/firestore/useDocumentOnce.d.ts:2:29 - error TS2307: Cannot find module '../util'.

2 import { LoadingHook } from '../util';
                              ~~~~~~~~~

initialising and error not working on Auth Hook

Hello there! :-)

I'm using the same code provided as example and the initialising and error states are never reached... login and logout are working fine.

Just found a related topic, but I installed the "react-firebase-hooks" packpage today so it shouldn't be happening.

Can't perform a React state update on an unmounted component

When I use useCollection and useDocumentOnce together in one function component I periodically get index.js:1375 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. warnings.

Here is one of the components that throw that warning:

const Channels = ({user, feedId}) => {
  const [
    userValue,
    userLoading,
    userError,
  ] = useDocumentOnce(firebase.firestore().doc('users/' + user.email), {
    snapshotListenOptions: {includeMetadataChanges: true},
  });
  const [
    value,
    loading,
    error,
  ] = useCollection (
    firebase
      .firestore ()
      .collection ('channels')
      .where ('feedId', '==', feedId),
    {
      snapshotListenOptions: {includeMetadataChanges: true},
    }
  );
  return (
    <div className="channels">
      <h1>Channels</h1>
      {error && <strong>Error: {JSON.stringify (error)}</strong>}
      {loading && <div>Loading Channels...</div>}
      {value && userValue &&
        <div>
          {value.docs
            .sort ((a, b) => {
              return a.id > b.id ? 1 : -1;
            })
            .filter(doc => userValue.data().channels.includes(doc.id))
            .map (doc => (
              <React.Fragment key={doc.id}>
                <div>
                  <button onClick={() => navigate ('/messages/' + doc.id)}>
                    {capitalize (doc.id)}
                  </button>
                </div>
              </React.Fragment>
            ))}
        </div>}
    </div>
  );
};

Here is the parent component:

const App = () => {
  const [user, loading, error] = useAuthState(firebase.auth());
  const logout = () => firebase.auth().signOut();
  return (
    <div className="collaboracast">
      <header className="collaboracast-header">
        <h3><Link to="/">Don-Nan</Link></h3>
        {user && <nav><span>{user.email}</span><button onClick={logout}>logout</button></nav>}
      </header>
      <main>
        {!loading && <Router className="app-router">
          {user && <Feeds path="/feeds" />}
          {user && <Channels path="/channels/:feedId" user={user} />}
          {user && <Messages path="/messages/:channelId" user={user} />}
          <Login default path="/" user={user} />
        </Router>}
        {loading && <div>loading...</div>}
      </main>
    </div>
  );
}

I don't think the useDocumentOnce returns a cleanup function.

How to get the access token

Hi,

Is there away to get the provider access token? Since we can only get it right after sign in and I cannot find any documentation on how to get it with our auth hooks.

Thanks,
H

Use undefined instead of void

It looks like the loading utility hook types the value property as void. This makes it difficult to work around potential edge cases in the code:

  let [ user, userIsLoading, userError ] = useAuthState(firebase.auth());
  if (userIsLoading) {
    return null;
  }
  // do something with user ...
  user.someProperty; // <- errors with "someProperty doesn't exist on type void | User"

The problem here is that, because user is typed as possibly being void (which is different from undefined), I can't even do something like user!.someProperty to assure TS that the variable will have a value.

I think the fix here is to switch to undefined as the fallback type.

Querying nested data

Hi, I have some data structured something like the following:

  • Root doc
    • Middle collection of node docs (only 1 layer deep for now)
      • Collection of leaf docs

Normally, I might break this up into separate components that each have their own hook, but I am using a library that accepts the data in hierarchical form. Is there a way I can query the whole structure at once? Alternatively, do people have best practices for structuring data like this so it can be queried in parts and then assembled in an efficient way (for reading, say)?

Playing around with it now, may update with work in progress as I go.

Q. Should hooks return an Object or an Array?

makes sense: snapshot.data(); snapshot.id

doesn't: value.data(); value.id

Just food for though. I see you pushed 1.0 so I doubt you wanna make breaking changes. I can just wrap anyway

Get data from firestore fields

hi I am new to react and firebase. just started using react-firebase-hooks
using react-firebase-hooks i successfully got all the data in the document.
i am wondering if i could get data individually.
(i.e)

{profile.email}

thanks in advance

import React from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import { useDocument } from "react-firebase-hooks/firestore";
import firebase from "firebase";

function Profile() {
  const [user] = useAuthState(firebase.auth());
  const [snapshot, loading, error] = useDocument(
    user
      ? firebase
          .firestore()
          .collection("users")
          .doc(user.uid)
      : null
  );

  return (
    <div className={classes.ap}>
      {error && <strong>Error: {JSON.stringify(error)}</strong>}
      {loading && <span>Document: Loading...</span>}
      {snapshot && <span>Document: {JSON.stringify(snapshot.data())}</span>}
    </div>
  );
}

export default Profile;

Pagination Example

Question: is there any way to paginate through firebase list using this library?

orderBy

How can I use orderBy? I'm trying to to use after .where with useCollection and does not work.

const [list, loading, error] = useCollection( firebase.db.collection('songs').where('placeId', '==', props.match.params.place).orderBy('createdAt'), { snapshotListenOptions: { includeMetadataChanges: true }, } );

Query using date comparator causes constant component reload

When using a query containing the 'where date' clause as below, the data loads correctly but the component and its children are constantly reloaded. Removing the .where('date', '>', Firebase.firestore.Timestamp.now()) stops this happening. I migrated to this project from another and this logic worked correctly there.

Is there anything I'm doing wrong?

Thanks

`
const withEvents = Component => props => {
const provider = useContext(CalProvider);
const _class = useContext(ClassProvider);

    console.log('withEvents loading');
    const [values, loading, error] = useCollectionData(
        Firebase.firestore()
            .collection(`providers/${provider.id}/events`)
            .where('classId', '==', _class.id)
            .orderBy('date', 'asc')
            .limit(4),{idField:'id'}
    );
    if (error) return <Error error={error} />;
    if (loading) return <CircularProgress />;
    
    if (values.length === 0) return <NoEvents />;

    return (
        <EventsProvider.Provider value={values}>
            <Component>{props.children}</Component>
        </EventsProvider.Provider>
    );
};`

Support for mutating data with hooks

I would like to see some mutation support for making changes in firebase not just reading data. I would look to apollo mutation hooks for inspiration. Ideas that I have in mind are along these lines:

function useDocumentUpdate<T>(docRef: firestore.DocumentReference): [(data: T) => void, boolean, Error | undefined] {
  const [mutating, setMutating] = useState(false)
  const [error, setError] = useState<Error  | undefined>(undefined)

  const updateDoc = (data: T) => {
    setMutating(true)
    docRef.update(data).catch(e => setError(e)).finally(() => setMutating(false))
  }

  return [updateDoc, mutating, error]
}

Possible useCollectionData() and useDocumentData()

I have found the collectionData() from rxfire to be super useful.

Currently, your hooks return a snapshot. While I don't find that very useful others might. So what if we added a new useCollectionData(query, idField='id') to the api.

Here is an example of how I wrap the useCollection from this library.

export const useCollectionData = (query, idField = 'id') => {
  const { error, loading, value } = useCollection(query)

  const docs = []
  if (!loading) {
    value.forEach(snap => {
      docs.push({
        ...snap.data(),
        [idField]: snap.id,
      })
    })
  }

  return [docs, loading, error]
}

There are two things to notice. The first is that I return a collection of documents NOT a snapshot. The second is that I return an array just like useState() from React. I find this very helpful when using multiple useCollections() with a Component.

The same feature could be added to the useDocument() hooks as well.

useCollection - chain requests by using previous data in refs

In the following code, you can see that the second useCollection requires data from the first, the organisation id. How can I chain these requests together so that the second request does not give an error, but instead waits for the first request to complete and then continue with the seconds request?

Thanks

const [organisation, loadingOrganisation, errorOrganisation] = useCollection(
    firebase.firestore().collection('organisations').where("members", "array-contains", user.uid),
    {
      snapshotListenOptions: { includeMetadataChanges: true },
    }
  );

  const [projects, loadingProjects, errorProjects] = useCollection(
    firebase.firestore().collection('organisations').doc(organisation[0].id).collection("projects"),
    {
      snapshotListenOptions: { includeMetadataChanges: true },
    }
  );

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

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.

useCollectionData always returns new values in array

useCollectionData returns new items in the array every time when a hook is called. This makes it impossible to perform an equality comparison on the values in the array because the values are always newly created.

Example:

const ComponentA = () => {
  const [value, loading] = useCollectionData(db.collection(`myCollection`));
  if (loading) return null
  return <ComponentB value={value} />
}

const ComponentB = (props) => {
  const firstItem = value[0]
  
  useEffect(() => {
    // effect is called always because firstItem is always different
  }, [firstItem])
}

Workaround:
I've made a custom hook which stores data to a local state.

export const useColData = (query, options) => {
  const [data, setData] = React.useState([]);
  const [value, loading, error] = useCollection(query, options);
  
  React.useEffect(() => {
    if (!value) return;
    setData(
      value.docs.map((doc) => {
        const docData = { ...doc.data() };
        if (options.idField) {
          docData[options.idField] = doc.id;
        }
        return docData;
      })
    );
  }, [value, options.idField]);

  return [data, loading, error];
};

Is it possible to change implementation of useCollectionData to return always the same items in the array if the query result is not changed?

Hydrate `Reference` type from Firestore

What is the currently recommended way to fetch references from within a loaded document snapshot?
Could we add an option to automatically hydrate child references into a snapshot or data object?

[Firestore] Include typescript intersection type for idField option on all use...Data hooks

For Firestore collections and docs, it would be nice if each of the ...Data hooks would automatically add the idField (if provided) as a key/value to the <T> generic.

For example, the useDocumentData hook would return something like T & { idField: string } instead of just T. In this case, idField should be represented by the actual string value entered into options.idField.

Does this make sense? I feel like it should be trivial to do something like this, but couldn't quite get it to work myself. I'd be happy to make a pull request if anyone has some extra insight.

Thanks!

Transition from non-authorized to authorized does not update collection content

I have a collection that requires an authenticated login to access. When I first view the collection in an un-authenticated state I of course do not get any content. If I subsequently authenticate, I expect the collection to populate with contents so useCollection will 'refresh'. However, this does not occur.

Is there a technique I can use to force useCollection to refresh content?

useCollection return an array where the value is of type any

In my component this code:

  const [value, loading, error] = useCollection(firebase.firestore().collection('projects'));

makes value an any type object.

As a workaround i have done this:

type TValue = firebase.firestore.QuerySnapshot | undefined;
cons MyComponent:React.FC = () => {
  const [value, loading, error] = useCollection(firebase.firestore().collection('projects'));
  const value = v as TValue;
  ...
}

As you can see from the image, v is any.
image

Why is this so? Is any the intended type for value, or is it a bug, or is there something I need to do?

Extract from package.json:

    // ...
    "firebase": "^6.3.4",
    "firebaseui": "^4.1.0",
    "next": "^9.0.1",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-firebase-hooks": "^2.1.0",
    "react-firebaseui": "^4.0.0",
   // ...

Firestore hooks - run function on data update

Is there a simple way to run a function every time a new snapshot is loaded by the client? I need to run a simple function that makes some changes to the data gotten from firestore, and then sets a useState value. Currently, you can only use the variable provided when you initialise a useCollection hook, meaning it is hard to format the data first before using the variable in JSX.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.