Resolving infinite loop while using withConverter
jaytavares opened this issue · 2 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!