createApp()

Creates a Stapp application.

Definitions

type createApp = (config: {
  name?: string
  modules: Array<Module | ModuleFactory>
  dependencies?: any
  rehydrate?: any
  middlewares?: Middleware[]
}) => Stapp

type Stapp<State, API> = {
  name: string
  subscribe: (state: State) => void
  api: API
  ready: Promise<Partial<State>>

  // Imperative and not recommended to use API
  // Still useful for testing
  dispatch: (event: any) => any
  getState: () => State
}

Usage

Create a Stapp application by combining one or more modules with createApp:

import { createApp } from 'stapp'
import { formBase } from 'stapp-formbase'
import { logger, counter, whatever } from '../modules'

export const app = createApp({
  name: 'My super app',
  modules: [
    formBase,
    logger,
    counter,
    whatever
  ]
})

Config

The only required field is modules.

name

string - Name is not required but recommended if you have more than one app (and you should have more than one app, in other cases, you might not need this library) and use redux-devtools.

modules

array - See more about modules in the corresponding section.

dependencies

object - Any function provided as a module will be called with the value of this field.

middlewares

array - An array of any custom redux middlewares.

rehydrate

object - Value provided here, will be used as an initial state.

devtools

object or false - you can provide your own configuration for redux-devtools here, or disable it completely. See redux-devtools documentation for more info.

Examples

Todos

// todoModule.js
import { createReducer } from 'stapp'

let id = 0
const uniqueId = () => id++
const todosReducer = createReducer([])

const events = todosReducer.createEvents({
  addTodo: (todos, todo) => todos.concat({id: uniqueId(), completed: false, text: todo }),
  deleteTodo: (todos, id) => todos.filter(todo => todo.id !== id),
  editTodo: (todos, payload) => todos.map(todo => todo.id === payload.id ? {...todo, text: payload.text } : todo),
  toggleComplete: (todos, id) => todos.map(todo => todo.id === id ? {...todo, completed: !todo.completed } : todo),
  removeCompleted: (todos) => todos.filter(todo => !todo.completed)
})

export const todoModule = {
  name: 'todos',
  state: {
    todos: todosReducer
  },
  api: events
}

// todoApp.js
import { createApp } from 'stapp'
import { todoModule } from './todoModule.js'

const todoApp = createApp({
  name: 'Todo',
  modules: [todoModule]
})

todoApp
    .subscribe(state => console.log(`State: ${JSON.stringify(state)}`))
// State: { todos: [] }

todoApp.api.addTodo('Add first todo')
// State: { todos: [
//    { id: 0, completed: false, text: 'Add first todo' }
// ] }

todoApp.api.editTodo({ id: 0, text: 'Wait, didnt I already did this?'})
// State: { todos: [
//    { id: 0, completed: false, text: 'Wait, didnt I already did this?' }
// ] }

todoApp.api.toggleComplete(0)
// State: { todos: [
//    { id: 0, completed: true, text: 'Wait, didnt I already did this?' }
// ] }

todoApp.api.removeCompleted()
// State: { todos: [] }

Todo with undo

Well, you easily can use excellent redux-undo higher order reducer, but where is the fun? This example shows you the real power of epics.

// undoModule.js
import { tap, map, mapTo, filter } from 'rxjs/operators'
import { createEvent, dangerouslyReplaceState } from 'stapp'

const undo = createEvent('Undo')
const redo = createEvent('Redo')

export const undoModule = () => {
  let prev = []
  let next = []

  // Here we save state changes on every event excepts undo, redo and
  // special Stapp event dangerouslyReplaceState because they
  // will be handled separatly
  const saveStoreEpic = (event$, state$, { getState }) => event$.pipe(
    filter(({ type }) => (
      type !== undo.getType() &&
      type !== redo.getType() &&
      type !== dangerouslyReplaceState.getType()
    )),
    tap(() => {
      prev.push(getState())
      next = []
    }),
    mapTo(null)
  )

  // Undo state changes
  const undoEpic = undo.epic((undo$, state$, { getState }) => undo$.pipe(
    filter(() => prev.length > 0),
    tap(() => next.push(getState())),
    map(() => dangerouslyReplaceState(prev.pop()))
  ))

  // Redo state changes
  const redoEpic = redo.epic((redo$, state$) => redo$.pipe(
    filter(() => next.length > 0),
    tap(() => prev.push(getState())),
    map(() => dangerouslyReplaceState(next.pop()))
  ))

  // undoModule is made as a function to store prev and next
  // arrays in the closure so that each application will have it's
  // own history.
  return {
    name: 'undo',
    api: {
      undo,
      redo
    },
    epic: [
      saveStoreEpic,
      undoEpic,
      redoEpic
    ]
  }
}

// todoApp.js
import { createApp } from 'stapp'
import { todoModule } from './todoModule.js'
import { undo } from './undoModule.js'

const todoApp = createApp({
  name: 'Todo',
  modules: [
    todoModule,
    undoModule()
  ]
})

todoApp.state$
  .subscribe(state => console.log(`State: ${JSON.stringify(state)}`))
// State: { todos: [] }

todoApp.api.addTodo('Add first todo')
// State: { todos: [
//    { id: 0, completed: false, text: 'Add first todo' }
// ] }

todoApp.api.undo()
// State: { todos: [] }

todoApp.api.redo()
// State: { todos: [
//    { id: 0, completed: false, text: 'Add first todo' }
// ] }

You can explore other examples in the examples section on GitHub.

results matching ""

    No results matching ""