Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 53 additions & 46 deletions src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import ProviderContext, {
export function createStore(state, id = uuid()) {
const result = {
id,
initialState: { ...state },
state,
subscriptions: new Set(),
context: createNamedContext(id),
destroy: () => {
Expand All @@ -23,21 +21,54 @@ export function createStore(state, id = uuid()) {
},
getState: () => result.state,
}

result.state = createState(result, {
setState: props => props,
...state,
})

return result
}

function createState(store, initialState) {
const setState = changes => {
store.state = { ...store.state, ...changes }
store.subscriptions.forEach(callback => callback(changes))
return true
}

return {
...initialState,
...wrapStateUpdateFunctions(initialState, store, setState),
}
}

function getStateUpdateFunctions(state) {
return Object.keys(state)
.filter(name => typeof state[name] === 'function')
.reduce(
(acc, name) => {
acc[name] = state[name]
return acc
},
{
setState: props => props,
.reduce((acc, name) => {
acc[name] = state[name]
return acc
}, {})
}

function wrapStateUpdateFunctions(state, store, callback) {
const actions = getStateUpdateFunctions(state)
return Object.keys(actions).reduce((acc, name) => {
const wrapped = actions[name]
acc[name] = (...args) => {
let result = wrapped(...args)
let isFunc = typeof result === 'function'
if (isFunc) result = result(store.state)
if (result.then) {
return Promise.resolve(result).then(callback)
} else {
return callback(result)
}
)
}

return acc
}, {})
}

export class RenderPure extends React.PureComponent {
Expand All @@ -57,49 +88,25 @@ export class Provider extends React.PureComponent {
super()
const { store, children, id, ...state } = props
this.store = store || createStore(state, id)
// When no store is given, create context by id or refer to the default context
if (!store)

// When no store is given,
// create context by id or refer to the default context
if (!store) {
this.store.context = id ? createNamedContext(id) : ProviderContext
// Overwrite the functions in store.state to update the state of this Provider
const actions = getStateUpdateFunctions(this.store.initialState)
Object.assign(
this.store.state,
Object.keys(actions).reduce(
(acc, name) => ({
...acc,
[name]: (...args) => {
let result = actions[name](...args)
let isFunc = typeof result === 'function'
if (isFunc) result = result(this.state)
if (result.then) {
return new Promise(res =>
Promise.resolve(result).then(state => {
// Update store
this.store.state = { ...this.store.state, ...state }
// Call subscribers
this.store.subscriptions.forEach(callback => callback(state))
// Update local state
this.setState(state, res)
})
)
} else {
// Update store in sync
this.store.state = { ...this.store.state, ...result }
this.store.subscriptions.forEach(callback => callback(result))
this.setState(result)
return true
}
},
}),
{}
)
)
}

// When a store was given,
// additional state could be different "initialState"

this.state = this.store.state
this.unsubscribe = this.store.subscribe(state => this.setState(state))
}

componentWillUnmount() {
this.unsubscribe()
if (this.props.id) removeNamedContext(this.props.id)
}

render() {
return (
<this.store.context.Provider
Expand Down
30 changes: 9 additions & 21 deletions tests/__snapshots__/store.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,6 @@ exports[`external store 1`] = `
"destroy": [Function],
"getState": [Function],
"id": "externalTest",
"initialState": Object {
"async": [Function],
"count": 0,
"functional": [Function],
"simple": [Function],
},
"state": Object {
"async": [Function],
"count": 0,
Expand All @@ -144,7 +138,9 @@ exports[`external store 1`] = `
"simple": [Function],
},
"subscribe": [Function],
"subscriptions": Set {},
"subscriptions": Set {
[Function],
},
}
}
>
Expand Down Expand Up @@ -188,12 +184,6 @@ exports[`external store 2`] = `
"destroy": [Function],
"getState": [Function],
"id": "externalTest",
"initialState": Object {
"async": [Function],
"count": 0,
"functional": [Function],
"simple": [Function],
},
"state": Object {
"async": [Function],
"count": 2,
Expand Down Expand Up @@ -246,15 +236,14 @@ exports[`external store, setState 1`] = `
"destroy": [Function],
"getState": [Function],
"id": "externalTest2",
"initialState": Object {
"count": 0,
},
"state": Object {
"count": 0,
"setState": [Function],
},
"subscribe": [Function],
"subscriptions": Set {},
"subscriptions": Set {
[Function],
},
}
}
>
Expand Down Expand Up @@ -295,15 +284,14 @@ exports[`external store, setState 2`] = `
"destroy": [Function],
"getState": [Function],
"id": "externalTest2",
"initialState": Object {
"count": 0,
},
"state": Object {
"count": 1,
"setState": [Function],
},
"subscribe": [Function],
"subscriptions": Set {},
"subscriptions": Set {
[Function],
},
}
}
>
Expand Down
42 changes: 18 additions & 24 deletions tests/__snapshots__/subscribe.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -908,15 +908,14 @@ exports[`subscribe(store) 1`] = `
"destroy": [Function],
"getState": [Function],
"id": "testStore",
"initialState": Object {
"message": "success!",
},
"state": Object {
"message": "success!",
"setState": [Function],
},
"subscribe": [Function],
"subscriptions": Set {},
"subscriptions": Set {
[Function],
},
}
}
>
Expand Down Expand Up @@ -949,15 +948,14 @@ exports[`subscribe(store) 1`] = `
"destroy": [Function],
"getState": [Function],
"id": "testStore",
"initialState": Object {
"message": "success!",
},
"state": Object {
"message": "success!",
"setState": [Function],
},
"subscribe": [Function],
"subscriptions": Set {},
"subscriptions": Set {
[Function],
},
}
}
>
Expand Down Expand Up @@ -994,15 +992,14 @@ exports[`subscribe(store, "state") 1`] = `
"destroy": [Function],
"getState": [Function],
"id": "testStore",
"initialState": Object {
"message": "success!",
},
"state": Object {
"message": "success!",
"setState": [Function],
},
"subscribe": [Function],
"subscriptions": Set {},
"subscriptions": Set {
[Function],
},
}
}
>
Expand Down Expand Up @@ -1039,15 +1036,14 @@ exports[`subscribe(store, "state") 1`] = `
"destroy": [Function],
"getState": [Function],
"id": "testStore",
"initialState": Object {
"message": "success!",
},
"state": Object {
"message": "success!",
"setState": [Function],
},
"subscribe": [Function],
"subscriptions": Set {},
"subscriptions": Set {
[Function],
},
}
}
>
Expand Down Expand Up @@ -1088,15 +1084,14 @@ exports[`subscribe(store, context => context) 1`] = `
"destroy": [Function],
"getState": [Function],
"id": "testStore",
"initialState": Object {
"message": "success!",
},
"state": Object {
"message": "success!",
"setState": [Function],
},
"subscribe": [Function],
"subscriptions": Set {},
"subscriptions": Set {
[Function],
},
}
}
>
Expand Down Expand Up @@ -1129,15 +1124,14 @@ exports[`subscribe(store, context => context) 1`] = `
"destroy": [Function],
"getState": [Function],
"id": "testStore",
"initialState": Object {
"message": "success!",
},
"state": Object {
"message": "success!",
"setState": [Function],
},
"subscribe": [Function],
"subscriptions": Set {},
"subscriptions": Set {
[Function],
},
}
}
>
Expand Down