CSFrequency / react-firebase-hooks

React Hooks for Firebase.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Resolving infinite loop while using withConverter

jaytavares opened this issue · comments

I'm trying to use the useDocumentData hook with a firestore data converter but am having trouble with infinite loops. Discussion in #158 suggested that I would have to declare the converter outside of the React component; however, I can't do that since the document path is dependent on app state that is only available from within the component.

Existing code (Causes infinite loop):

const QuickBooksSettings = (props: QuickBooksSettingsProps) => {
  const { business } = useAppState()

  const [value, loading, error] = useDocumentData(
    doc(
      getFirestore(),
      `businesses/${business.bid}/integrations/quickbooks`
    ).withConverter(convertTo<QuickBooksIntegrationDocument>()), // <--- problem line
    {
      snapshotListenOptions: { includeMetadataChanges: false }
    }
  )

  // . . .

  return (
    !loading && (
      <AccountHeader
        companyName={value?.settings.companyName}
      />
      // . . . 
    )
  )
}

export default QuickBooksSettings

Suggested solution from #158 (Impossible due to inaccessible variable):

// ERROR: business.bid not available
const ref = doc(getFirestore(),
  `businesses/${business.bid}/integrations/quickbooks`
).withConverter(convertTo<QuickBooksIntegrationDocument>())

const QuickBooksSettings = (props: QuickBooksSettingsProps) => {
  const { business } = useAppState()

  const [value, loading, error] = useDocumentData(
    ref,
    {
      snapshotListenOptions: { includeMetadataChanges: false }
    }
  )

  // . . .
  
  return (
    !loading && (
      <AccountHeader
        companyName={value?.settings.companyName}
      />
      // . . . 
    )
  )
}

export default QuickBooksSettings

Attempted solution (Still causes infinite loop):

function getRef(bid: string) {
  return doc(
    getFirestore(),
    `businesses/${bid}/integrations/quickbooks`
  ).withConverter(convertTo<QuickBooksIntegrationDocument>())
}

const QuickBooksSettings = (props: QuickBooksSettingsProps) => {
  const { business } = useAppState()

  const [value, loading, error] = useDocumentData(getRef(business.bid)), // <-- refactor into helper func; PROBLEM: Still causes infinite loop
    {
      snapshotListenOptions: { includeMetadataChanges: false }
    }
  )

  // . . .
  
  return (
    !loading && (
      <AccountHeader
        companyName={value?.settings.companyName}
      />
      // . . . 
    )
  )
}

export default QuickBooksSettings

Any thoughts on how I can still use the converter with this document reference?

Your issue is that convertTo<QuickBooksIntegrationDocument>() creates a new instance of the converter each time. Rather than pulling out the whole doc construction, you can simply pull the converter creation out to a variable as so:

const converter = convertTo<QuickBooksIntegrationDocument>();

const QuickBooksSettings = (props: QuickBooksSettingsProps) => {
  const { business } = useAppState()

  const ref = doc(getFirestore(),
    `businesses/${business.bid}/integrations/quickbooks`
  ).withConverter(converter);

  const [value, loading, error] = useDocumentData(
    ref,
    {
      snapshotListenOptions: { includeMetadataChanges: false }
    }
  )

  // . . .
  
  return (
    !loading && (
      <AccountHeader
        companyName={value?.settings.companyName}
      />
      // . . . 
    )
  )
}

export default QuickBooksSettings

@chrisbianca 👏 Perfect. Thanks!