developit / unistore

🌶 350b / 650b state container with component actions for Preact & React
https://npm.im/unistore
2.86k stars 139 forks source link

Mutations #161

Closed krzepah closed 5 years ago

krzepah commented 5 years ago

A Mutation system for Unistore.

I'm not convinced my self this should be merged in, as unused paradigm shouldn't be pushed in production. Here's the fork called mutastore ; It will all depend on your feedback.

Introduction

Why : I wanted to bind a listener to the store to synchronise every state change online. As for today, we only retrieve updated state and the update that triggered this.

Now here lies the problem, when we use a list of elements, that update contains the entire list of elements; So sending this trough was not really a good idea.

What : I needed to retrieve only the few parameters that were actually added to the store. Hence, the easiest way to achieve this was to change the actions so they don't return the update, but rather which mutation it should call and it's parameters.

So what changed to be short ?

Listeners receive a "mutation command" rather than the full update

They are in the form

{ 'mutationKey': {...params} }

The first key of the object is used a a mutation key, while it's data is used as args. Every other key is used as an additional parameter to pass trough to the listeners.

Mutations are in the form

const mutations = {
  mutationKey: (state, {...params}) => ({...yourUsualDataMutation})
}

Oh boy you want to break all of our apps ?

Mutation is an optional paradigm that is enabled only if a mutation parameter is sent to createStore.

Even if mutations are passed to the store, setState should work as intended as it received no change.

Unfortunately mutation usage is rather radical since any action return would then be considered as a mutation ; .setState can still be used to make a direct change in the store.

A new mutate function is also available, it can only work if a mutation object is passed to the store.

/**
 * Triggers a mutation
 * @param act is an object containing mutation key and it's params
 * @overwrite allow to overwrite the state by the mutation result
 */
function mutate(act, overwrite) {
    let key = Object.keys(act)[0];
    setState(mutations[key](state, act[key]), overwrite, act);
}

Cost

full/preact.js : 760b to 818b = 58b full/unistore.js : 400b to 412b = 12b Total : 70b

Example

Before

store.subscribe((state, action) => console.log(action))

const Base = < div>< /div>
const CBase = connect(
  (state, props) => {...state},
  (state, props) => {
     addElement: (state, e, id) => ({
       elements: merge(state.elements, {...e}),
       elementsIds: concat(state.elementsIds, id)
  }
)(Base)
Output
{ 
  elements: {
     a,
     ...
     z
   },
   elementsIds : [a...z]
}

After

const mutations = {
  newElement: (state, {text, newId}) => ({
    elements: merge({
      [newId]: text
    }, state.elements),
    elementsIds: concat([newId], state.elementsIds)
  })
}

let store = createStore(defaults, mutations)

store.subscribe((state, action) => console.log(act))

const Base = < div>< /div>
const CBase = connect(
(state, props) => {...state},
  (state, props) => {
     addElement: (state, e, id) => { 'newElement': {...e, ...id} }
)(Base)
Output
{ 'newElement': { 'text': 'foo', id: 'bar' } }