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 22, 2019
1 parent 09c19ed commit 71e1d7a
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 13 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)
* [Dependency 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 @@
# Dependency injection

## Explanation

Dependency injection is a concept for reuse some dependencies based on the running context. Good for Reatom users - all basic entities (actions, atoms) are already working in a controlled context - a store. By the same token, any atom dependencies (as another atom) are automatically adding to a store when computed atom connecting to it. That means you already have dependencies resolving mechanism with Reatom.

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

## Example

> IMPORTANT NOTE: in order to prevent errors in serialization **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())
```
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@reatom/core",
"private": false,
"version": "1.0.0",
"version": "1.0.1",
"description": "State manager with a focus of all needs",
"source": "src/index.ts",
"main": "build/index.js",
Expand Down
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 71e1d7a

Please sign in to comment.