Frameworks
ElectricSQL is designed to integrate with existing reactive component and frameworks/libraries and ORMs. It also provides a notifier API for generic integrations.
React
ElectricSQL integrates with React via a context provider and hooks. You can see this in action by looking at the source code of the React Native example app.
ElectricProvider
context provider
In React, context provides a way to pass data through the component tree without having to pass props down manually at every level. A Context.Provider is a wrapper component that’s used to pass context down to its descendents in the component hierarchy.
ElectricSQL provides an ElectricProvider
that accepts an electrified database driver instance as it’s value. Use it as one of the root components that wrap your app. For example, the code below illustrates an ExampleApp
that opens and electrifies a database client and then provides it as the context value to the wrapped child components.
import React, { useEffect, useState } from 'react'
import SQLite from 'react-native-sqlite-storage'
import { electrify } from 'electric-sql/react-native'
import { ElectricProvider, useElectric, useElectricQuery } from 'electric-sql/react'
import config from '.electric/@config'
export const ExampleApp = () => {
const [ db, setDb ] = useState()
useEffect(() => {
const promisesEnabled = true
SQLite.enablePromise(promisesEnabled)
SQLite.openDatabase('example.db')
.then((original) => electrify(original, promisesEnabled, config))
.then((db) => setDb(db))
}, [])
if (db === undefined) {
return null
}
return (
<ElectricProvider db={db}>
{/* child components go here */}
</ElectricProvider>
)
}
With an ElectricProvider
in place, you can then interact with the db
via hooks.
useElectric
hook
The useElectric
hook simply returns the db
value, which you can then use as you wish. For example below we insert a row with a random value into the database every time a user presses a button.
import React from 'react'
import { Pressable, Text, View } from 'react-native'
import { useElectric } from 'electric-sql/react'
const ExampleComponent = () => {
const db = useElectric()
const insertItem = () => {
const randomValue = Math.random().toString(16).substr(2)
db.transaction((tx) => {
tx.executeSql('INSERT INTO items VALUES(?)', [randomValue])
})
}
return (
<View>
<Pressable onPress={addItem}>
<Text>Insert</Text>
</Pressable>
</View>
)
}
useElectricQuery
hook
useElectricQuery
sets up a live query (aka a dynamic or reactive query). This takes an sql statement, optional bind parameters and keeps the results in sync whenever data changes.
import React from 'react'
import { Text, View } from 'react-native'
import { useElectricQuery } from 'electric-sql/react'
const ExampleComponent = () => {
const { results } = useElectricQuery('SELECT value FROM items', [])
return (
<View>
{results.map((item, index) => (
<Text key={ index } style={styles.item}>
Item: { item.value }
</Text>
))}
</View>
)
}
The full return value of the hook is:
const { results, error, updatedAt } = useElectricQuery(sql, bindParams?)
Running the query successfully will assign a new array of rows to the results
and error
will be undefined
. Or if the query errors, the error will be assigned to the error
variable and results
will be undefined
. The updatedAt
variable is a Date instance set when the return value last changed. Which is either when the query is first run or whenever it’s re-run following a data change event.
See the implementation in frameworks/react/hooks.ts for more details.
Putting it together
See the React Native and React examples in the example applications repo.
Prisma
Prisma supports SQLite. However, it doesn’t yet support SQLite in the client. As such, support is limited to the Node driver.
Todo: publish Prisma example and document usage here.
TypeORM
Todo: publish TypeORM example and document usage here.
Generic
It’s possible to use the Notifier API to integrate ElectricSQL with any reactive component framework. The trick is to subscribe to data change events and re-query when query results may have been affected.
Because you’re querying a local database, it’s usually very fast to perform a query. Typically it’s faster to just ask the database than to maintain a caching layer. For example, the implementation in the useElectricQuery
hook simply monitors changes by tablename.
const handleChange = (notification: ChangeNotification): void => {
// Reduces the `ChangeNotification` to an array of namespaced tablenames,
// in a way that supports both the main namespace for the primary database
// and aliases for any attached databases.
const changedTablenames = notifier.alias(notification)
if (hasIntersection(tablenames, changedTablenames)) {
bustCache()
}
}
const key = notifier.subscribeToDataChanges(handleChange)
Next steps
- get started by signing up for an account and creating an ElectricSQL application