robinweser / alveron

Tiny (0.8kb) Elm-inspired state management for React
https://alveron.js.org
MIT License
73 stars 2 forks source link

Middleware Proposal #3

Closed robinweser closed 1 year ago

robinweser commented 6 years ago

Middleware

This PR adds a minimal API for adding middleware. Middleware are functions that receive the new state and return a (modified) new state.

import { createStore } from 'alveron'

const model = 0
const actions = {
  increment: state => state + 1,
  decrement: state => state - 1
}

const { Provider, Consumer } = createStore({ 
  model, 
  actions
})

const logger = (state, context) => {
  console.log(state)
  console.log(context)
  return state
}

const Usage = () => (
  <Provider middleware={[logger]}>
    <Consumer>{({ state, actions }) => (
      <div>
        Count {state}
        <button onClick={actions.increment}>+</button>
        <button onClick={actions.decrement}>-</button>   
    )}</Consumer>
  </Provider>
)

Doing the following:

  1. Click on +
  2. Click on +
  3. Click on -

will trigger the listeners, which logs the current state:

1
{ action: 'increment', payload: [], previousState: 0 },
2
{ action: 'increment', payload: [], previousState: 1 },
1
{ action: 'decrement', payload: [], previousState: 2 },
robinweser commented 6 years ago

@farskid What do you think on this one? Might be a better idea to add it to the createStore method as well, having a single source of truth that can be customized etc.

farskid commented 6 years ago

I like the idea of having middlewares. is there a specific reason behind the name listeners? cause it seems to me, they are middlewares and not subscribe funcs, right?

robinweser commented 6 years ago

Actually they’re more like subscribe functions as they do not alter the state (thats what actions, effects are for).

They should rather be used for logging, caching etc.

Or is there a good reason why they should be able to alter state?

farskid commented 6 years ago

OK, I've got a proposal:

What if we have the middlewares in a way that matches the middleware interface of Redux? a function that accepts store => next => action.

There a couple of pros on this approach:

  1. It's a familiar convention considering Express, Redux, ...
  2. It eases the migration from Redux to WoodWorm (if that's what we aim for)
  3. All middlewares from Redux can be ported to WoodWorm with no effort.
  4. It solves the issue of async actions (effects) by using old solutions in redux such as Thunk and Saga.

What do you think?

robinweser commented 6 years ago

That's not possible as we need to have synchronous middleware as it's executed within React's async setState already... We could have middleware with the following API: (currentState) => nextState but that's all here.

All other options would drastically change the current behaviour which is too verbose for me as that's the whole point with this micro package.

farskid commented 6 years ago

That is true! (currentState) => [compose_all_middlewares_resutls_here] => nextState is a fine API. we can do the setState as the final step on nextState in order not to get into the pitfall on async state updates.

robinweser commented 6 years ago

@farskid I updated the PR, would love to hear your thoughts :)

farskid commented 6 years ago

This sounds like a promising start.

question: Does middleware get called on effects too? Am I missing something here?

robinweser commented 6 years ago

Effects now actually dont mutate state directly, but call actions instead which then triggers middleware again ;)