Typescript

Generics, optionele parameters, typeof en veel meer

const handleUpdate = useCallback(
  async (
    docId: string,
    data: Partial<T>,
    options?: {
      batch?: Firebase.firestore.WriteBatch,
    }
  ) => {
    options?.batch
      ? options.batch.update(collection.doc(docId), data)
      : await collection.doc(docId).update(data);
    if (!options || !options.batch) {
      setState((d) => {
        const index = d.docs.findIndex((doc) => doc.id === docId);
        d.docs[index] = {
          ...d.docs[index],
          ...data,
        };
        return {
          loading: d.loading,
          docs: [...d.docs],
        };
      });
    }
  },
  [collection]
);
een Firebase batch update doen met useCallback

Google Firebase implementatie

Collectie data ophalen met een react custom hook.

export function useCollection<T extends {}>(collectionPath: string, queryCallback?: (collection: Firebase.firestore.CollectionReference) => Firebase.firestore.Query | Firebase.firestore.CollectionReference) {
  const [{ docs, loading }, setState] = useState<{ loading: boolean, docs: Array<T & { id: string }> }>({ loading: false, docs: [] })
  const firestore = useFirestore()
  const collection = useMemo(() => firestore.collection(collectionPath), [firestore, collectionPath])

  useEffect(() => {
    setState({ loading: true, docs: [] })
    const snapshot = queryCallback ? queryCallback(collection).get() : collection.get()
    snapshot.then(s => {
      setState(() => ({ loading: false, docs: s.docs.map(doc => ({ id: doc.id, ...doc.data() as T })) }))
    })
  }, [collection, queryCallback])
custom hooks om Firebase collectie data op te halen

Authenticatie event afluisteren en state wijzigen

export function useFirebaseAuthUser(authStateChangedCallback?: (user: Firebase.User | null) => void) {
  const [user, setUser] = useState<Firebase.User | null>(null)
  const auth = useAuth()
  useEffect(() => {
    auth.onAuthStateChanged(user => {
      setUser(user)
      authStateChangedCallback && authStateChangedCallback(user)
    })
  }, [auth, authStateChangedCallback])
  return user
}
Firebase authenticatie wrappen met custom hook

React Material UI

<AutComplete /> component om treinstation locaties op te zoeken

import { useLazyQuery } from '@apollo/client'
import { TextField } from '@material-ui/core'
import { Autocomplete } from '@material-ui/lab'
import React, { useCallback, useEffect, useMemo } from 'react'
import { QUERY_PLACES } from '../queries'

let timeout: NodeJS.Timeout

function PlaceSearch<T extends { name: string, type: string }>({ endAdornment, label, margin, value, onChange }: { endAdornment?: React.ReactNode, label: React.ReactNode, margin?: 'dense' | 'none' | 'normal', value: T | null, onChange: (event: React.ChangeEvent<{}>, value: T | null) => void }) {
  const [getPlaces, { loading, data }] = useLazyQuery<{ places: T[] }>(QUERY_PLACES)
  useEffect(() => {
    return function cleanup() {
      clearTimeout(timeout)
    }
  }, [])
  const search = useCallback((q: string) => {
    if (value) {
      getPlaces({ variables: { q: value.name } })
    } else {
      clearTimeout(timeout)
      timeout = setTimeout(() => {
        if (q.length > 2) {
          getPlaces({ variables: { q } })
        }
      }, 500)
    }
  }, [getPlaces, value])
  const filteredOptions = useMemo(() => {
    if (data) {
      const stations = data.places.filter(({ type }) => type === 'stationV2')
      return stations.length > 0 ? stations : data.places
    }
    return []
  }, [data])
  return <Autocomplete
    renderInput={(props) => (
      <TextField
        {...props}
        label={label}
        margin={margin}
        variant="outlined"
        InputProps={{ ...props.InputProps, style: { paddingRight: 8 }, endAdornment }}
      />
    )}
    value={value}
    loading={loading}
    loadingText="Zoeken..."
    noOptionsText="Zoek naar een station"
    onChange={onChange}
    onInputChange={(e, v) => search(v)}
    options={filteredOptions}
    getOptionLabel={opt => opt.name}
    getOptionSelected={(opt, v) => opt.name === v.name}
  />
}

export default PlaceSearch
React Material UI autocomplete component vullen met NS Api data

NS Api stations ophalen en in de state zetten

export async function getPlaces(q?: string, station_code?: string, lat?: number, long?: number) {
  const find = q ? { q } : station_code ? { station_code } : { lat, lng: long }
  const response = await fetch(`${apiUrl}/places-api/v2/places?limit=100&${serialize(find)}&radius=5000&lang=nl&type=stationV2`, { headers }) // removed: stop,address
  const data: { payload: any[] } = await response.json()
  return data.payload.reduce((places, { type, locations }) => {
    locations.forEach(({ stationCode, UICCode, link, name, lat, lng, ...rest }: any) => places.push({ ...rest, name, UICCode, stationCode, link: link.uri, type, position: { lat, long: lng } }))
    return places
  }, [])
}
NS Stations ophalen met Fetch en dan als hook wrappen

Meer code voorbeelden:

some hooks wrapping firebase client lib
some hooks wrapping firebase client lib. GitHub Gist: instantly share code, notes, and snippets.
Travel assistent project i’ve been working on recently
Travel assistent project i’ve been working on recently - NSApi.ts