Skip to content

dogstore #93

@ahdinosaur

Description

@ahdinosaur

a random idea to re-write the core of dogstack

dogstore

opinionated redux using a composition of tiny modules that do one thing well:

  • redux-action-registry
  • reselect-registry
  • redux-state-reactor in progress
  • feathers-action@3
  • redux-action-query
  • modified redux-thunk

redux-action-registry

no more importing redux action creators around,

dispatch an action with an action creator name and arguments,

a friendly middleware will to create and dispatch the action for you.

const { createStore, applyMiddleware } = require('redux')
const { action, middleware: actionRegistryMiddleware } = require('redux-action-registry')
const reducer = require('./reducer')

const actions = {
  yoga: () => ({ type: 'YOGA' }),
  eat: (food) => ({ type: 'EAT', food })
  sleep: (length) => ({ type: 'SLEEP', length })
}

const store = createStore(
  reducer,
  applyMiddleware(
    actionRegistryMiddleware(actions)
  )
)

store.dispatch(action('yoga'))
store.dispatch(action('eat', 'burritos'))
store.dispatch(action('sleep', '10h))

source hints:

function action (actionCreator, ...args) {
  return { actionCreator, args }
}

reselect-registry

no more importing reselect selectors around,

create selectors from the names of other selectors.

const _ = require('lodash')
const combineSelectors = require('reselect-registry')

const usersData = state => state.users
const thingsData = state => state.things

// reselect.createSelector
const thingsByUsers = [
  'things',
  things => _.groupBy(things, 'userId')
]

const users = [
  'users',
  'thingsByUser',
  (users, thingsByUser) => {
    return _.mapValues(users, user => {
      const things = thingsByUser[user.id]
      return _.assign(user, { things })
    })
  }
)

// reselect.createStructuredSelector
const usersPageProps = {
  users: true
}

const selectors = combineSelectors({
  usersData,
  thingsData,
  thingsByUsers,
  users,
  usersPageProps
})

redux-state-reactor

reactors are similar to selectors,

they receive the state and return either an action or false.

const { createStore, applyMiddleware } = require('redux')
const { middleware: actionReactorMiddleware } = require('redux-action-reactor')

const actions = require('./actions')

const reducer = (state = {}, action) => {
  if (action.type === 'AUTHENTICATE_START') {
    return Object.assign(state, { isAuthenticating: true })
  } else if (action.type === 'AUTHENTICATE_COMPLETE') {
    return Object.assign(state, { isAuthenticating: false, isAuthenticated: true })
  }
  /* ... */
}

const authenticationReactor = state => {
  if (state.isAuthenticated) return false
  if (state.isAuthenticating) return false // to avoid reactor cycle
  return actions.authenticate()
}

const reactors = [
  authenticateReactor
]

const store = createStore(
  reducer,
  applyMiddleware(
    actionReactorMiddleware(authenticationReactor)
  )
)

// with empty state, will automatically start authenticating!

feathers-action@3

good ideas from feathers-action@2:

  • keep all request state in feathers key
  • design for testing

changes:

  • id of request is hash of arguments
  • use feathers-thunk instead of redux-observable
  • service method call api
    • things.get(id, params), etc
    • return promises
  • service listener api
    • things.onCreated({ dispatch, filter })
    • listeners are implicitly restricted by feathers channels
    • filter using similar techniques as feathers-reactive

redux-action-query

  • smart dependent queries
  • assume request state is stored in stateKey
    • status
      • start
      • error
      • done
    • result
    • error
const actions = require('./actions')

const queries = [
  {
    name: 'users',
    action: (state) => actions.fetchUsers()
  }
  {
    name: 'thingsForUsers',
    dependencies: [
      'users'
    ],
    action: (state) => {
      const users = state.users
      return action.fetchThingsForUsers(users)
    }
  }
]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions