Skip to content

Commit

Permalink
feat(model): create models repeatedly will not override existing models
Browse files Browse the repository at this point in the history
  • Loading branch information
ArrayZoneYour committed Sep 28, 2021
1 parent b22c2d4 commit 5621226
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 17 deletions.
60 changes: 60 additions & 0 deletions __test__/Model/same-name.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/// <reference path="../index.d.ts" />
import { renderHook } from '@testing-library/react-hooks'
import { NextCounter } from '..'
import { Model } from '../../src'

describe('useStore', () => {
test('create by single model definition', async () => {
let state: any
let actions: any
let mirrorState: any
let mirrorActions: any
let count = 0
const { useStore, subscribe, unsubscribe, getState } = Model({
NextCounter
})
const {
useStore: useMirrorStore,
subscribe: mirrorSubscribe,
unsubscribe: mirrorUnSubscribe,
getState: getMirrorState
} = Model({ NextCounter })
renderHook(() => {
;[state, actions] = useStore('NextCounter')
;[mirrorState, mirrorActions] = useMirrorStore('NextCounter')
})
expect(state).toEqual({ count: 0 })
expect(mirrorState).toEqual({ count: 0 })

mirrorSubscribe('NextCounter', 'increment', () => (count += 1))

await actions.increment(3)
expect(state).toEqual({ count: 3 })
expect(mirrorState).toEqual({ count: 0 })
expect(count).toBe(0)

await mirrorActions.increment(3)
expect(state).toEqual({ count: 3 })
expect(mirrorState).toEqual({ count: 3 })
expect(count).toBe(1)

// test subscribe
subscribe('NextCounter', 'increment', () => (count += 1))
await actions.increment(4)
expect(count).toBe(2)
expect(state.count).toBe(7)
expect(mirrorState.count).toBe(3)
expect(getState('NextCounter').count).toBe(7)
expect(getMirrorState('NextCounter').count).toBe(3)

// test unsubscribe
unsubscribe('NextCounter', 'increment')
mirrorUnSubscribe('NextCounter', 'increment')
await actions.increment(3)
expect(state.count).toBe(10)
expect(mirrorState.count).toBe(3)
expect(getState('NextCounter').count).toBe(10)
expect(getMirrorState('NextCounter').count).toBe(3)
expect(count).toBe(2)
})
})
2 changes: 2 additions & 0 deletions src/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ let withDevTools = false
let uid = 0 // The unique id of hooks
let storeId = 0 // The unique id of stores
let currentStoreId = '0' // Used for useModel
let gid = 0 // The unique id of models' group

export default {
Actions,
Expand All @@ -36,6 +37,7 @@ export default {
devTools,
subscriptions,
mutableState,
gid,
uid,
storeId,
currentStoreId,
Expand Down
9 changes: 5 additions & 4 deletions src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,19 @@ const timeout = <T>(ms: number, data: T): Promise<T> =>
}, ms)
)

const getInitialState = async <T extends { modelName: string }>(
const getInitialState = async <T extends { modelName: string | string[] }>(
context?: T,
config?: { isServer?: boolean }
config?: { isServer?: boolean; prefix?: string }
) => {
const ServerState: { [name: string]: any } = { __FROM_SERVER__: true }
await Promise.all(
Object.keys(Global.State).map(async (modelName) => {
let prefix = config?.prefix || ''
if (
!context ||
!context.modelName ||
modelName === context.modelName ||
context.modelName.indexOf(modelName) !== -1
modelName === prefix + context.modelName ||
context.modelName.indexOf(prefix + modelName) !== -1
) {
const asyncGetter = Global.AsyncState[modelName]
const asyncState = asyncGetter ? await asyncGetter(context) : {}
Expand Down
1 change: 1 addition & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ interface Global {
Setter: Setter
devTools: any
withDevTools: boolean
gid: number
uid: number
storeId: number
currentStoreId: string
Expand Down
46 changes: 33 additions & 13 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,16 @@ function Model<M extends Models, MT extends ModelType, E>(
useStore: (selector?: Function) => useStore(hash, selector)
}
} else {
if (initialState) {
// TODO: support multi model group under SSR
Global.gid = 1
} else {
Global.gid += 1
}
let prefix = ''
if (Global.gid > 1) {
prefix = Global.gid + '_'
}
if (models.actions) {
console.error('invalid model(s) schema: ', models)
const errorFn = (fakeReturnVal?: unknown) => (..._: unknown[]) => {
Expand All @@ -196,8 +206,10 @@ function Model<M extends Models, MT extends ModelType, E>(
})
}
extContext && (Global.Context['__global'] = extContext)
Object.keys(models).forEach((name) => {
const model = models[name]
let actions: { [name: string]: any } = {}
Object.keys(models).forEach((n) => {
let name = prefix + n
const model = models[n]
if (model.__ERROR__) {
// Fallback State and Actions when model schema is invalid
console.error(name + " model's schema is invalid")
Expand Down Expand Up @@ -239,12 +251,9 @@ function Model<M extends Models, MT extends ModelType, E>(
Global.Middlewares[name] = Global.Middlewares[model.__id]
Global.Context[name] = Global.Context[model.__id]
}
})

const actions = Object.keys(models).reduce(
(o, modelName) => ({ ...o, [modelName]: getActions(modelName) }),
{}
)
actions[n] = getActions(name)
})

Global.withDevTools =
typeof window !== 'undefined' &&
Expand All @@ -255,12 +264,23 @@ function Model<M extends Models, MT extends ModelType, E>(
}
return {
actions,
getActions,
getInitialState,
getState,
subscribe,
unsubscribe,
useStore
getActions: (name: string) => getActions(prefix + name),
getInitialState: async <T extends { modelName: string | string[] }>(
context?: T,
config?: { isServer?: boolean }
) => getInitialState(context, { ...config, prefix }),
getState: (name: string) => getState(prefix + name),
subscribe: (
name: string,
actions: keyof MT['actions'] | Array<keyof MT['actions']>,
callback: () => void
) => subscribe(prefix + name, actions as string | string[], callback),
unsubscribe: (
name: string,
actionName: keyof MT['actions'] | Array<keyof MT['actions']>
) => unsubscribe(prefix + name, actionName as string | string[]),
useStore: (name: string, selector?: Function) =>
useStore(prefix + name, selector)
} as APIs<M>
}
}
Expand Down

0 comments on commit 5621226

Please sign in to comment.