This documentation is for the old Kea 0.28. To see the latest docs, click here!

kea(options)

Create a new kea logic store and connect it to redux.

Usage

Here is a complete example with all the options available. See below for further explanations.

// scenes/my-random-scene/logic.js
import { kea } from 'kea'
import localStoragePlugin from 'kea-localstorage'

import otherLogic from './other-logic.js'
import dynamicLogic from './dynamic-logic.js'

export default kea({
  plugins: [
    localStoragePlugin
  ],

  connect: {
    actions: [
      otherLogic, [
        'firstAction',
        'secondAction as renamedAction'
      ]
    ],

    props: [
      otherLogic, [
        'firstProp',
        'secondProp as renamedProp',
        '* as allProps'
      ],
      // use Dynamic Logic
      dynamicLogic.withKey(props => props.id), [
        'dynamicProp as thatProp'
      ],
      // select from any redux tree node
      (state) => state.somethingThatResolvesToAnObject, [
        'variable',
        'otherVariable'
      ]
    ]
  },

  path: () => ['scenes', 'myRandomScene', 'index'],

  constants: () => ['STRING', 'OTHER_STRING'],

  actions: ({ path, constants }) => ({
    actionWithStaticPayload: 'payload value',
    anotherActionWithAStaticPayload: { thisIs: 'that' },
    simpleAction: true,

    actionWithDynamicPayload: (id) => ({ id }),
    actionWithManyParameters: (id, message) => ({ id, message }),
    actionWithObjectInput: ({ id, message }) => ({ id, message })
  }),

  reducers: ({ actions, path, constants }) => ({
    reducerKey: [defaultValue, propType, /* with localStoragePlugin: { persist: true }, */ {
      // operations
      [actions.simpleAction]: (state, payload) => state + payload.value // return the new state,
      [actions.complexAction]: (state, payload) => {
        // do more things in the block
        return state + payload.value
      },
      [actions.noStateUsed]: (_, payload) => payload.value,
      [actions.setToTrue]: () => true,
      [actions.clearSomething]: () => false,
      "ANY_OTHER_ACTION_TYPE": (state, payload, meta) => 'do whatever you want'
    }],

    constantDefault: [constants.OTHER_STRING, PropTypes.string, {
      [actions.clearSomething]: () => constants.STRING,
      [actions.someOtherAction]: (_, payload) => payload.value
    }]
  }),

  selectors: ({ path, constants, actions, selectors }) => ({
    selectorName: [
      () => [selectors.inputSelector1, selectors.inputSelector2],
      (input1, input2) => createOutput(input),
      returnPropType
    ],

    computedValue: [
      () => [selectors.reducerKey, selectors.constantDefault],
      (reducerKey, constantDefault) => {
        return complicatedOperation(reducerKey, constantDefault)
      },
      PropTypes.object
    ]
  })
})

// index.js
import myRandomSceneLogic from 'scenes/my-random-scene/logic'

Options

plugins: []

Plugins that only this logic store will use. See the plugins array in getStore on how to install global plugins.

// Input
plugins: [
  localStoragePlugin
]

path: () => []

Give a name to the logic store and register it in a certain location in your application's Redux tree.

// Input
path: () => ['scenes', 'myRandomScene', 'logicMountPoint']

// Output
myRandomSceneLogic.path == ['scenes', 'myRandomScene', 'logicMountPoint']

constants: () => []

Create constants that can be used in other parts of the logic store.

// Input
constants: () => ['STRING', 'OTHER_STRING']

// Output
myRandomSceneLogic.constants == { STRING: 'STRING', OTHER_STRING: 'OTHER_STRING' }

actions: ({ path, constants }) => ({})

Define action creators

// Input
actions: ({ path, constants }) => ({
  actionWithStaticPayload: 'payload value',
  anotherActionWithAStaticPayload: { thisIs: 'that' },
  simpleAction: true,

  actionWithDynamicPayload: (id) => ({ id }),
  actionWithManyParameters: (id, message) => ({ id, message }),
  actionWithObjectInput: ({ id, message }) => ({ id, message })
})

// Output
myRandomSceneLogic.actions == {
  actionWithStaticPayload: () => ({ type: '...', payload: { value: 'payload value' } }),
  anotherActionWithAStaticPayload: () => ({ type: '...', payload: { thisIs: 'that' } }),
  simpleAction: () => ({ type: '...', payload: { value: true } }),

  actionWithDynamicPayload: (id) => ({ type: '...', payload: { id } }),
  actionWithManyParameters: (id, message) => ({ type: '...', payload: { id, message } }),
  actionWithObjectInput: ({ id, message }) => ({ type: '...', payload: { id, message } })
}

reducers: ({ path, constants, actions }) => ({})

Define the structure and logic of your reducers

// Input
reducers: ({ actions, path, constants }) => ({
  reducerKey: [defaultValue, propType, /* optional options: { persist: true }, */ {
    // operations
    [actions.simpleAction]: (state, payload) => state + payload // return the new state,
    [actions.complexAction]: (state, payload) => {
      // do more things in the block
      return state + payload
    },
    [actions.noStateUsed]: (_, payload) => payload.value,
    [actions.setToTrue]: () => true,
    [actions.clearSomething]: () => false,
    "ANY_OTHER_ACTION_TYPE": (state, payload, meta) => 'do whatever you want'
  }],

  constantDefault: [constants.OTHER_STRING, PropTypes.string, {
    [actions.clearSomething]: () => constants.STRING,
    [actions.someOtherAction]: (_, payload) => payload.value
  }]
})

// Output
myRandomSceneLogic.reducers == {
  reducerKey: (initialState = defaultValue, action) => /* ... */,
  constantDefault: (initialState = constants.OTHER_STRING, action) => /* ... */,
}
myRandomSceneLogic.reducer == combineReducers(reducers)

selectors: ({ path, constants, actions, selectors }) => ({})

Define selectors, which are only recomputed when their input changes

// Input
selectors: ({ path, constants, actions, selectors }) => ({
  selectorName: [
    () => [selectors.inputSelector1, selectors.inputSelector2],
    (input1, input2) => createOutput(input),
    returnPropType
  ],

  computedValue: [
    () => [selectors.reducerKey, selectors.constantDefault],
    (reducerKey, constantDefault) => {
      return complicatedOperation(reducerKey, constantDefault)
    },
    PropTypes.object
  ]
})

// Output
myRandomSceneLogic.selectors == {
  // all reducer keys first,
  reducerKey: (state) => state.scenes.myRandomScene.index.reducerKey,
  constantDefault: (state) => state.scenes.myRandomScene.index.constantDefault,

  // other defined selectors
  selectorName: (state) => memoizedSelectorForSelectorName(state),
  computedValue: (state) => memoizedSelectorForComputedValue(state)
}

myRandomSceneLogic.selector == (state) => ({
  reducerKey: state.scenes.myRandomScene.index.reducerKey,
  constantDefault: state.scenes.myRandomScene.index.constantDefault,
  selectorName: memoizedSelectorForSelectorName(state),
  computedValue: memoizedSelectorForComputedValue(state)
})

connect: {}

Fetch actions and selectors/props from other logic stores.

// Input
connect: {
  actions: [
    otherLogic, [
      'firstAction',
      'secondAction as renamedAction'
    ]
  ],

  props: [
    otherLogic, [
      'firstProp',
      'secondProp as renamedProp',
      '* as allProps'
    ],
    // use Dynamic Logic
    dynamicLogic.withKey(props => props.id), [
      'dynamicProp as thatProp'
    ],
    // select from any redux tree node
    (state) => state.somethingThatResolvesToAnObject, [
      'variable',
      'otherVariable'
    ]
  ]
}

// Output
myRandomSceneLogic.actions == {
  firstAction: otherLogic.actions.firstAction,
  renamedAction: otherLogic.actions.secondAction
}

myRandomSceneLogic.selectors == {
  firstProp: otherLogic.selectors.firstProp,
  renamedProp: otherLogic.selectors.secondProp,
  allProps: otherLogic.selector,
  variable: state.somethingThatResolvesToAnObject.variable,
  otherVariable: state.somethingThatResolvesToAnObject.otherVariable
}