@taraai/read-write
Version:
Synchronous NoSQL/Firestore for React
85 lines (78 loc) • 2.88 kB
JavaScript
import { isEqual, differenceWith } from 'lodash'
import { useMemo, useEffect, useRef } from 'react'
import { watchEvents, unWatchEvents } from './actions/query'
import { getEventsFromInput, invokeArrayQuery } from './utils'
import useFirebase from './useFirebase'
/**
* @description Hook that automatically listens/unListens to provided firebase paths
* using React's useEffect hook.
* @param {Function|Array} queriesConfig - Object, string, or
* array contains object or string for path to sync from Firebase or null if
* hook doesn't need to sync. Can also be a function that returns an object,
* a path string, or array of an object or a path string.
* @see https://react-redux-firebase.com/docs/api/useFirebaseConnect.html
* @example <caption>Ordered Data</caption>
* import React from 'react'
* import { useSelector } from 'react-redux'
* import { useFirebaseConnect } from 'react-redux-firebase'
*
* export default function Todos() {
* // sync /todos from firebase into redux
* useFirebaseConnect(['todos'])
* // Connect to redux state using selector hook
* const todos = useSelector(state => state.firebase.data.todos)
* return (
* <div>
* {JSON.stringify(todos, null, 2)}
* </div>
* )
* }
* @example <caption>Data that depends on props</caption>
* import React from 'react'
* import { compose } from 'redux'
* import { useSelector } from 'react-redux'
* import { useFirebaseConnect } from 'react-redux-firebase'
*
* export default function Post({ postId }) {
* useFirebaseConnect([`posts/${postId}`]) // sync /posts/postId from firebase into redux
* const post = useSelector(({ firebase: { ordered: { posts } } }) => posts && posts[postId])
* return (
* <div>
* {JSON.stringify(post, null, 2)}
* </div>
* )
* }
*/
export default function useFirebaseConnect(queriesConfig) {
const firebase = useFirebase()
const eventRef = useRef()
const dataRef = useRef()
const data = useMemo(() => invokeArrayQuery(queriesConfig), [queriesConfig])
useEffect(() => {
if (data !== null && !isEqual(data, dataRef.current)) {
const itemsToSubscribe = differenceWith(data, dataRef.current, isEqual)
const itemsToUnsubscribe = differenceWith(dataRef.current, data, isEqual)
dataRef.current = data
// UnWatch all current events
unWatchEvents(
firebase,
firebase.dispatch,
getEventsFromInput(itemsToUnsubscribe)
)
// Get watch events from new data
eventRef.current = getEventsFromInput(data)
// Watch new events
watchEvents(
firebase,
firebase.dispatch,
getEventsFromInput(itemsToSubscribe)
)
}
}, [data])
// Emulate componentWillUnmount
useEffect(() => {
return () => {
unWatchEvents(firebase, firebase.dispatch, eventRef.current)
}
}, [])
}