Skip to content

Commit

Permalink
feat(core): #38 di
Browse files Browse the repository at this point in the history
  • Loading branch information
artalar committed Dec 21, 2019
1 parent 09c19ed commit ba518d8
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 12 deletions.
7 changes: 4 additions & 3 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@
* [@reatom/debug](/packages/debug.md)

- Guides
* [Naming Conventions](/guides/naming-conventions.md)
* [File Structure](/guides/file-structure.md)
* [Migration from Redux](/guides/migration-from-redux.md)
* [Code Splitting](/guides/code-splitting.md)
* [Server Side Rendering](/guides/server-side-rendering.md)
* [Migration from Redux](/guides/migration-from-redux.md)
* [Dependencie injection](/guides/di.md)
* [Naming Conventions](/guides/naming-conventions.md)
* [File Structure](/guides/file-structure.md)

* [Examples](/examples.md)

Expand Down
38 changes: 38 additions & 0 deletions docs/guides/di.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Dependencie injection

## Explanation

Dependencie injections is a concept for reuse some dependencies based on the running context. Good for Reatom users - all basic entities (actions, atoms) is a already work in controlled context - a store. By the same token any atom dependencies (as an other atoms) are autamaticaly adding to a store when computed atom connecting to it. That means you already has dependencies resoving mechanism with Reatom.

What is it mean in conclusion? You can create not only reactive atoms (depended from actions) but just _static_ atoms and use it as a services.

## Example

> IMPORTANT NOTE: in order to prevent errors in serelisation **use a symbol** as an atom key
```ts
/* DECLARE API SERVICE */

import { declareAtom, declareAction, createStore } from '@reatom/core'
import axios from 'axios'

export const API = Symbol('api')
export const apiAtom = declareAtom(API, axios, () => [])

/* SOME FEATURE CODE */

export const recieveData = declareAction()
export const requestData = declareAction(async (payload, store) => {
const data = await store.getState(apiAtom).get(url, payload)
store.dispach(recieveData(data))
})

/* TESTS */

// here we rewrite initial state (axios) of apiAtom by preloaded state
const store = createStore({
[API]: mockedAxios
})
// ...
store.dispatch(requestData())
```
7 changes: 4 additions & 3 deletions packages/core/src/declareAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const DEPS = Symbol('@@Reatom/DEPS')
const _initAction = declareAction(['@@Reatom/init'])
export const initAction = _initAction()

type AtomName = string | [TreeId] | symbol
type AtomsMap = { [key: string]: Atom<any> }
type Reducer<TState, TValue> = (state: TState, value: TValue) => TState
type DependencyMatcher<TState> = (
Expand All @@ -36,12 +37,12 @@ export function declareAtom<TState>(
dependencyMatcher: DependencyMatcher<TState>,
): Atom<TState>
export function declareAtom<TState>(
name: string | [TreeId],
name: AtomName,
initialState: TState,
dependencyMatcher: DependencyMatcher<TState>,
): Atom<TState>
export function declareAtom<TState>(
name: string | [TreeId] | TState,
name: AtomName | TState,
initialState: TState | DependencyMatcher<TState>,
dependencyMatcher?: DependencyMatcher<TState>,
): Atom<TState> {
Expand All @@ -51,7 +52,7 @@ export function declareAtom<TState>(
name = 'atom'
}

const _id = nameToId(name as string | [TreeId])
const _id = nameToId(name as AtomName)

if (initialState === undefined)
throwError(`Atom "${_id}". Initial state can't be undefined`)
Expand Down
17 changes: 11 additions & 6 deletions packages/core/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Atom } from './declareAtom'
import { PayloadActionCreator } from './declareAction'

export { TreeId }
export type GenId = (name: string | [string]) => TreeId
export type GenId = (name: string | [string] | symbol) => TreeId
export const TREE = Symbol('@@Reatom/TREE')

export type Unit = { [TREE]: Tree }
Expand All @@ -14,8 +14,10 @@ export type Unit = { [TREE]: Tree }
* type MyAtomType = InferType<typeof myAtom>
* type MyActionType = InferType<typeof myAction>
*/
export type InferType<T> = T extends (Atom<infer R> | PayloadActionCreator<infer R>)
? R
export type InferType<T> = T extends (
| Atom<infer R>
| PayloadActionCreator<infer R>)
? R
: never

export function noop() {}
Expand All @@ -37,13 +39,16 @@ export function getIsAction(thing: any): thing is Atom<any> {
}

let id = 0
export function nameToIdDefault(name: string | [string]): TreeId {
return Array.isArray(name)
export function nameToIdDefault(name: string | [string] | symbol): TreeId {
return typeof name === 'symbol'
? // TODO: https://github.com/microsoft/TypeScript/issues/1863
((name as unknown) as string)
: Array.isArray(name)
? safetyStr(name[0], 'name')
: `${safetyStr(name, 'name')} [${++id}]`
}
let _nameToId: GenId
export function nameToId(name: string | [string]): TreeId {
export function nameToId(name: string | [string] | symbol): TreeId {
return _nameToId ? _nameToId(name) : nameToIdDefault(name)
}

Expand Down
15 changes: 15 additions & 0 deletions packages/core/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,21 @@ describe('@reatom/core', () => {
})
})

test('DI example', () => {
class Api {}
const api = new Api()
const apiAtom = declareAtom(Symbol('API'), api, () => [])

var store = createStore(apiAtom)
expect(store.getState()).toEqual({
[getTree(apiAtom).id]: api,
})

var store = createStore({ [getTree(apiAtom).id]: api })
expect(store.getState(apiAtom)).toBe(api)
expect(JSON.stringify(store.getState())).toBe('{}')
})

test('declareAction reactions', async () => {
const delay = () => new Promise(on => setTimeout(on, 10))
const setValue = declareAction<number>()
Expand Down

0 comments on commit ba518d8

Please sign in to comment.