I have a very basic custom hook that takes in a path an returns a document from firebase
import React, { useState, useEffect, useContext } from 'react'; import { FirebaseContext } from '../sharedComponents/Firebase'; function useGetDocument(path) { const firebase = useContext(FirebaseContext) const [document, setDocument] = useState(null) useEffect(() => { const getDocument = async () => { let snapshot = await firebase.db.doc(path).get() let document = snapshot.data() document.id = snapshot.id setDocument(document) } getDocument() }, []); return document } export default useGetDocument
Then I use useEffect as a componentDidMount/constructor to update the state
useEffect(() => { const init = async () => { let docSnapshot = await useGetDocument("products/" + products[selectedProduct].id + "labels/list") if(docSnapshot) { let tempArray = [] for (const [key, value] of Object.entries(docSnapshot.list)) { tempArray.push({id: key, color: value.color, description: value.description}) } setLabels(tempArray) } else { setLabels([]) } await props.finishLoading() await setLoading(false) } init() }, [])
However, I get an Invariant Violation from "throwInvalidHookError" which means that I am breaking the rules of hooks, so my question is whether you can't use custom hooks inside useEffect, or if I am doing something else wrong.
No. Custom Hooks are a mechanism to reuse stateful logic (such as setting up a subscription and remembering the current value), but every time you use a custom Hook, all state and effects inside of it are fully isolated.
You should leverage the useEffect hook's dependency in your custom useFetchMyApi hook to refresh the list of items. Add some local component state to represent the "currently" selected location. Use your button's onClick callback to update the state and trigger a rerender which then will rerun your custom hook.
Rather than litter your components with a bunch of useState calls to keep track of the state of an async function, you can use our custom hook which takes an async function as an input and returns the value , error , and status values we need to properly update our UI.
Hooks are reusable functions. When you have component logic that needs to be used by multiple components, we can extract that logic to a custom Hook. Custom Hooks start with "use".
As far as I know, the hooks in a component should always be in the same order. And since the useEffect
happens sometimes and not every render that does break the rules of hooks. It looks to me like your useGetDocument
has no real need.
I propose the following solution:
useGetDocument
the same.useEffect
that has the document
as a dependency.Your component could look like the following:
const Component = (props) => { // Your document will either be null (according to your custom hook) or the document once it has fetched the data. const document = useGetDocument("products/" + products[selectedProduct].id + "labels/list"); useEffect(() => { if (document && document !== null) { // Do your initialization things now that you have the document. } }, [ document ]); return (...) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With