Skip to content

Instantly share code, notes, and snippets.

@mariusandra
Last active April 16, 2021 13:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mariusandra/26f2c550026a3bee98a107447e0fb99f to your computer and use it in GitHub Desktop.
Save mariusandra/26f2c550026a3bee98a107447e0fb99f to your computer and use it in GitHub Desktop.
// This is a crude implementation of a graphql plugin for kea.
// It needs to be cleaned up, tested and then made into a real plugin.
// There is basic support for subscriptions and reloading.
// Mutations are not implemented
// To use:
// - logicWithGraphQL.values.course == { id: 123, { translations: [...] } }
// - logicWithGraphQL.values.courseVariables == { id: 123 }
import gql from 'graphql-tag'
import { kea } from 'kea'
const logicWithGraphQL = kea({
queries: ({ props }) => ({
course: {
query: gql`
query CourseQuery($id: ID!) {
course(id: $id) {
id
translations {
title
language {
locale
}
}
}
}
`,
variables: props.id ? { id: props.id } : state => ({
id: globalRouterLogic.selectors.params(state).id // or whatever
}),
transform: data => data.course // so we don't have to do values.course.course.id
}
})
})
/// the plugin:
import { camelize } from 'humps'
const client = new ApolloClient({ ... })
const emptyVariables = {} // so it's always the same pointer
export default {
name: 'graphql',
defaults: () => ({
queries: {},
graphql: {
observableQueries: {},
querySubscriptions: {}
}
}),
events: {
beforeLogic (logic, input) {
if (input.queries) {
const queries = input.queries(logic)
Object.entries(queries).forEach(([key, { query, transform, variables }]) => {
const reloadAction = camelize(`reload-${key}`)
const successAction = camelize(`reload-${key}-success`)
const failureAction = camelize(`reload-${key}-failure`)
const variablesKey = `${key}Variables`
logic.extend({
actions: () => ({
[reloadAction]: true,
[successAction]: response => ({ response }),
[failureAction]: error => ({ error })
}),
reducers: ({ actions }) => ({
// store as { loading, error, data } ???
[key]: [
null,
{
[actions[successAction]]: (state, payload) => payload.response
}
],
[`${key}IsLoading`]: [
false,
{
[actions[reloadAction]]: () => true,
[actions[successAction]]: () => false,
[actions[failureAction]]: () => false
}
]
}),
selectors: ({ selectors }) => ({
[variablesKey]: [
() => (variables ? [state => variables(state)] : []),
variables => variables || emptyVariables
]
}),
listeners: ({ actions, values, store, props }) => ({
[actions[reloadAction]]: async function () {
const vars = values[variablesKey] || {}
logic.graphql.observableQueries[key].refetch(vars)
console.log('reload action triggered')
}
}),
events: ({ actions, values }) => ({
beforeMount () {
const vars = values[variablesKey] || {}
try {
if (!logic.queries[key]) {
logic.queries[key] = query
const observable = client.watchQuery({
query,
variables: vars
})
logic.graphql.observableQueries[key] = observable
logic.graphql.querySubscriptions[key] = observable.subscribe(function ({
data,
isLoading,
networkStatus,
stale
}) {
console.log(`${key} subscription returned something`, { data, isLoading, networkStatus, stale })
if (data) {
const result = transform ? transform(data) : data
actions[successAction](result)
}
})
}
const response = logic.graphql.observableQueries[key].getCurrentResult()
if (response.data) {
const result = transform ? transform(response.data) : response.data
actions[successAction](result)
}
} catch (error) {
actions[failureAction](error)
}
},
beforeUnmount (logic) {
if (
logic.graphql &&
logic.graphql.querySubscriptions &&
Object.keys(logic.graphql.querySubscriptions).length > 0
) {
Object.values(logic.graphql.querySubscriptions).forEach(querySubscription => {
querySubscription.unsubscribe()
})
}
}
})
})
})
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment