dlasky / kea

High level abstraction between React and Redux

Home Page:https://kea.js.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

NPM Version minified minified + gzipped Backers on Open Collective Sponsors on Open Collective

Kea Logo

A kea is two things:

  1. An extremely smart mountain parrot from New Zealand.
  2. 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.

Thank you to our sponsors!

Support this project by becoming a sponsor.

Your logo will show up here and on kea.js.org with a link to your website.

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

  1. be wrapped around your component or pure function:
const logic = kea({ /* options from above */ })

class Counter extends Component {
  render () {
    const { counter, doubleCounter } = this.props
    const { increment, decrement } = this.actions

    return <div>...</div>
  }
}

export default logic(Counter)
  1. used as decorators:
@kea({ /* options from above */ })
export default class Counter extends Component {
  render () {
    return <div />
  }
}

or

  1. imported and then connected to:
// features-logic.js
import { kea } from 'kea'
export default kea({ /* options from above */ })

// index.js
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:

# if you're using yarn
yarn add kea redux react-redux reselect

# if you're using npm
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:

// store.js
import { getStore } from 'kea'

export default getStore({
  // additional options (e.g. middleware, reducers, ...)
})

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:

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'; // <--- add this

import './index.css';

import store from './store'; // <--- add this BEFORE App
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!

Contributors

This project exists thanks to all the people who contribute. [Contribute].

Backers

Thank you to all our supporters! 🙏 [Become a backer]

About

High level abstraction between React and Redux

https://kea.js.org/

License:MIT License


Languages

Language:JavaScript 100.0%