

A kea
is two things:
- An extremely smart mountain parrot from New Zealand.
- An equally smart architecture for frontend webapps, built on top of React and Redux.
Try it out
Open the documentation (AKA demo app) and view its source on GitHub.
In the documentation you will find several examples with source. Check it out!
No, really, check out the docs!
What is Kea?
Kea is a state management library for React. It empowers Redux, making it as easy to use as setState
while retaining composability and improving code clarity.
- 100% Redux: Built on top of redux and reselect.
- Side effect agnostic: use thunks with redux-thunk, sagas with redux-saga or (soon!) epics with redux-observable.
- Wrappable: Write logic alongside React components. Easier than
setState
and perfect for small components. - Connectable: Pull in data and actions through ES6+ imports. Built for large and ambitious apps.
- No boilerplate: Forget
mapStateToProps
and redundant constants. Only write code that matters! - No new concepts: Use actions, reducers and selectors. Gradually migrate existing Redux applications.
Compare it to other state management libraries: Kea vs setState, Redux, Mobx, Dva, JumpState, Apollo, etc.
How does it work?
In Kea, you define logic stores with the kea({})
function.
Each logic store contains actions
, reducers
and selectors
.
kea({
actions: ({}) => ({ }),
reducers: ({ actions }) => ({ }),
selectors: ({ selectors }) => ({ })
})
They work just like in Redux:
- They are all pure functions (no side effects, same input = same output)
- Actions are functions which take an input and return a payload
- Reducers take actions as input and return new_data = old_data + payload
- Selectors take the input of multiple reducers and return a combined output
See here for a nice overview of how Redux works: Redux Logic Flow — Crazy Simple Summary
For example, to build a simple counter:
kea({
actions: () => ({
increment: (amount) => ({ amount }),
decrement: (amount) => ({ amount })
}),
reducers: ({ actions }) => ({
counter: [0, PropTypes.number, {
[actions.increment]: (state, payload) => state + payload.amount,
[actions.decrement]: (state, payload) => state - payload.amount
}]
}),
selectors: ({ selectors }) => ({
doubleCounter: [
() => [selectors.counter],
(counter) => counter * 2,
PropTypes.number
]
})
})
The logic stores can either
- be wrapped around your component or pure function:
const logic = kea({ })
class Counter extends Component {
render () {
const { counter, doubleCounter } = this.props
const { increment, decrement } = this.actions
return <div>...</div>
}
}
export default logic(Counter)
- used as decorators:
@kea({ })
export default class Counter extends Component {
render () {
return <div />
}
}
or
- imported and then connected to:
import { kea } from 'kea'
export default kea({ })
import { connect } from 'kea'
import featuresLogic from 'features-logic'
@connect({
actions: [
featuresLogic, [
'increment',
'decrement'
]
],
props: [
featuresLogic, [
'counter',
'doubleCounter'
]
]
})
export default class Counter extends Component {
render () {
return <div />
}
}
You can also connect logic stores together, to e.g:
... use actions from one logic store in the reducer of another.
... combine reducers from multiple logic stores into one selector.
Eventually you'll need side effects. Then you have a choice.
You can use simple thunks via redux-thunk:
import 'kea-thunk'
import { kea } from 'kea'
const incrementerLogic = kea({
actions: () => ({
increase: true
}),
reducers: ({ actions }) => ({
counter: [0, PropTypes.number, {
[actions.increase]: (state, payload) => state + 1
}]
}),
thunks: ({ actions, dispatch, getState }) => ({
increaseAsync: async (ms) => {
await delay(ms)
await actions.increase()
}
})
})
.... or the more powerful sagas via redux-saga.
(coming soon: support for epics with redux-observable)
Check out the examples on the homepage or start reading the guide for more.
If you're already using Redux in your apps, it's really easy to migrate.
Installation
First install the packages:
yarn add kea redux react-redux reselect
npm install kea redux react-redux reselect --save
Then configure the Redux store. You may either do it manually or use the getStore
helper. We recommend using the helper, as it will also configure any installed plugins (e.g. kea-saga). You may pass additional middleware and reducers as options.
First, create a file called store.js
with the following content:
import { getStore } from 'kea'
export default getStore({
})
Then import this in your app's entrypoint before any calls to kea()
are made. In practice this means you should import your store before your root component.
Finally, wrap your <App />
with Redux's <Provider />
.
This is how your entrypoint would look like if you used create-react-app
:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import './index.css';
import store from './store';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(
<Provider store={store}> // <-- add this
<App />
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
Scaffolding
You may also use the CLI tool to start a new Kea project:
npm install kea-cli -g
kea new my-project
cd my-project
npm install # or yarn
npm start # or yarn start
and open http://localhost:2000/.
Later inside my-project
run these to hack away:
kea g scene-name # new scene
kea g scene-name/component-name # component under the scene
kea g scene-name/component-name/really-nested # deeply nested logic
More documentation coming soon! Please help if you can!