react-garden
Version:
React + TypeScript + ThreeJS app using Material UI on NextJS, Apollo Client, GraphQL + WordPress REST APIs, for ThreeD web development.. a part of the threed.ai code family.
158 lines (136 loc) • 4.48 kB
text/typescript
// ** "apollo-reactive-store": "0.0.4"
import { makeVar, ReactiveVar, useReactiveVar } from '@apollo/client'
// [MM] COLORFUL CONSOLE MESSAGES (ccm)
import { ccm0, ccm1, ccm2, ccm3, ccm4, ccm5, ccm6 } from '~/@core/utils/console-colors'
type State<T> = Record<string | symbol, T>
type Store<T> = Record<string | symbol, ReactiveVar<T>>
type TypePolicies = {
Query: {
fields: {
[key: string]: {
read(): any
}
}
}
}
type Updater<Value> = (state: Value) => Value | Value
export interface StoreApi<Value> {
getState<T>(store: string | symbol): Object
get<T>(key: string | symbol): Value
update<StateSlice>(key: string | symbol, value: Updater<Value>): Value
useStore<T>(key: string | symbol): Value
getTypePolicies(): TypePolicies
reset(): void
}
export default function create<Value>(initialState: State<Value>, options = { debug: false }): StoreApi<Value> {
const createStore = () => {
return Object.keys(initialState).reduce<Store<Value>>((sum, key) => {
return {
...sum,
[key]: makeVar<Value>(initialState[key]),
}
}, {})
}
let store = createStore()
// DEBUGGING
if (options.debug) {
// console.debug(`store`, store)
// console.debug(`initialState`, initialState)
// console.debug(`initialState._type`, initialState._type)
if (initialState._type === 'scene') {
// returns Object {fields}
const fields: Object | any = {} // starts blank
const keys: string[] = Object.keys(store)
// console.debug('keys', keys)
keys.forEach((key: string, index: number) => {
fields[key] = store[key]()
// console.debug(`${index}: ${key}: ${store[key]()} = ${fields[key]}`)
})
// returns Array of Objects {fields}[]
// const fields = Object.keys(store).map((key: string) => {
// return { [key]: store[key]() }
// })
// not working here, for reference only
// returns Array of Objects {fields}[]
// const fields = Object.keys(store).reduce((sum, key) => {
// return {
// ...sum,
// [key]: {
// read() {
// return store[key]()
// },
// },
// }
// })
// console.debug(`${initialState._type}Store current state {fields}`, fields)
// console.debug('%c====================================', ccm5)
}
}
const debug = (key: string, value: Updater<Value>): void => {
if (options.debug) {
console.debug(`store update(key) "${key}" with value: ${JSON.stringify(value)}`)
}
}
return {
getState(): Object {
// returns Object {fields}
const fields: Object | any = {} // starts blank
const keys: string[] = Object.keys(store)
// console.debug('keys', keys)
keys.forEach((key: string, index: number) => {
fields[key] = store[key]()
// console.debug(`${index}: ${key}: ${store[key]()} = ${fields[key]}`)
})
if (!fields) {
throw new Error(`store getState(): "${JSON.stringify(store)}" is invalid`, store)
}
console.debug(`store getState(): {fields}`, fields)
return fields
},
get(key: string) {
const reactiveVar = store[key]
if (!reactiveVar) {
throw new Error(`store get(key) "${key}" is invalid`)
}
return reactiveVar()
},
update(key: string, value) {
const reactiveVar = store[key]
if (!reactiveVar) {
throw new Error(`store update(key) "${key}" is invalid`)
}
debug(key, value)
if (value instanceof Function) {
return reactiveVar(value(reactiveVar()))
} else {
return reactiveVar(value)
}
},
useStore(key: string) {
const reactiveVar = store[key]
if (!reactiveVar) {
throw new Error(`useStore: key "${key}" is invalid`)
}
return useReactiveVar(reactiveVar)
},
getTypePolicies() {
return {
Query: {
fields: Object.keys(store).reduce((sum, key) => {
return {
...sum,
[key]: {
read() {
return store[key]()
},
},
}
}, {}),
},
}
},
reset: () => {
store = createStore()
},
}
}