Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How does Kea compare to other state management frameworks? #106

Open
mariusandra opened this issue May 14, 2020 · 13 comments
Open

How does Kea compare to other state management frameworks? #106

mariusandra opened this issue May 14, 2020 · 13 comments

Comments

@mariusandra
Copy link
Member

Hi everyone! I get often asked to compare Kea with X, where X is some other approach to managing state in React. Be it easy-peasy, redux-toolkit, mobx, dva, react context + hooks, etc. (incomplete list)

Unfortunately I never have a good answer, as I haven't used any of them extensively and for big projects. I have just read the readmes, which is not enough for a smart comparison.

I believe Kea is something special in the react world and that it strikes a beautiful balance between simplicity vs flexibility vs joy vs power. However, while many people I've spoken to also seem to agree with this, I'm obviously really biased here.

Thus, I'm asking the internet: please share if you have any experiences with any of the other frameworks... and especially with how they compare to Kea when writing large real world applications, where state management gets pretty complex. I'm not interested in TodoMVC.

Your answers can be anything from "Kea rocks" to "Functional programming is the scourge of the earth and you're better off burning coal for heat" to "In a huge app with X state trees, we ran into issues X, Y, Z with framework B after we did Q, N and K".

Please keep it civil though!

My hope for this thread is that I can either: 1) learn something new to make Kea even better... or 2) prove that it really is as awesome as it seems!

I'll leave you with two quotes:

  • "A complex system that works is invariably found to have evolved from a simple system that worked." John Gall
  • "The best way to get the right answer on the internet is not to ask a question; it's to post the wrong answer" Ward Cunningham

Kea is a complex system that works as well as it does because it rose out of a simpler system over time. Kea 2.0 made this complex system much simpler to use and as a result, I think it's the best way to manage state in the React ecosystem.

Prove me wrong!

@examosa
Copy link

examosa commented May 14, 2020

I find myself often agonizing over this topic all the time. Am I using the best state management system/library for my use case? Is there one that's best for most/all use cases?
I know some awesome lists exist with regards to React state management (1, 2), which just go to show how many competing standards there are.
For my two most recent personal projects, my experiences were something like this:

  1. Started with Hooks + Context. As things became more complex I decided I needed something more robust. Switched to redux-toolkit and haven't had any regrets thus far.
  2. Older project that I was refactoring. Originally used Redux (rather poorly, I put everything in the store). Started refactoring to use rematch. Realized that it was overkill, so now I'm using mobx. I might not even need that, might just wind up using Hooks + Context; I've come full circle.

Overall I would say that it's pretty arbitrary for personal projects so long as it works and doesn't have a drastic negative impact on dx or performance. As for bigger projects i.e. those that will potentially be continued and maintained by others, I'm inclined to go with whatever has the best dx and community support. For me right now that's redux-toolkit. I'll be more inclined to give Kea a try on a new project once TypeScript support is added.

@Josema
Copy link

Josema commented May 17, 2020

Hi, I think this thread is very interesting. I didn't know about Kea until today, and it seems a good solution. But I can't really see the difference between things until I see them in practice. Would you mind to implement with Kea this dumb example I did to try Recoil?
https://codesandbox.io/s/three-buttons-example-with-recoil-qiqhf

@mariusandra
Copy link
Member Author

Hi @Teomik129 and @Josema!

@Teomik129, thanks for sharing your experience! I submitted Kea to both awesome state management lists. I hadn't heard of rematch until now, but despite their slogan of "Rematch is Redux best practices without the boilerplate." it seems to come with quite a bit of boilerplate. Namely those mapStateToProps and mapDispatchToProps functions and the dispatch superfunction. Code splitting with it also seems like a problem you'll have to patch together yourself.

Using context for state seems like a cool approach. I hadn't read that article before, but it seems like this can work well for small apps. However, React-redux in their v7 refactor migrated away from using context due to performance reasons. I also recently had a chat with someone (hi @lovekaizen) who said that they're running into issues with context as in their app every component gets re-rendered when something in the context changes.

--

Hi @Josema, thanks for writing! Recoil seems to be the new hype these last days. It's a great idea to compare it and Kea. I implemented the example you gave with Kea here:
https://codesandbox.io/s/kea-three-buttons-fbfv6?file=/src/app.js

There are more than one way to do this with kea, so I did two implementations there. logic1.js is the way I'd probably do it, but if needed, logic2.js is also an option.

@Josema
Copy link

Josema commented May 17, 2020

Thank you for taking the time to implement it in kea!

Two question, is it mandatory to use react-redux as Provider? Is not an extra kb for the bundle just for that?

Why the components render/print twice on every click? I mean, when I click on the red button, console.log should print:

<Red> 
<Magenta> 

But is printing:

<Red> 
<Red> 
<Magenta> 
<Magenta> 

@mariusandra
Copy link
Member Author

Hi @Josema

  1. Yes, we need react-redux as Kea relies heavily on react-redux's hooks (useSelector, useDispatch) to do its work. The same with the connect function for class based components. All the state fetching in the React side, which includes logic that determines what component needs to be updated and when is delegated to react-redux.
  2. The double printing seems to be a bug. I'm not sure why it's there, but this shouldn't happen. I'll investigate it shortly.

@mariusandra
Copy link
Member Author

Hi again @Josema , I figured out what was up with the double render. The codesandbox wrapped the <Provider><App /></Provider> tags with <React.StrictMode> in index.js.

The docs on StrictMode say the following:

Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:

  • Function component bodies
  • (and some others)

So the double render was caused by React making sure the app is compatible with its upcoming concurrent mode. This <StrictMode /> tag is there by default if you use create-react-app to create your app, and I guess it just got left in.

Mystery solved! :)

@Josema
Copy link

Josema commented May 19, 2020

That's cool. I see is working properly now.

I was thinking on creating a performance test to see how Kea and other frameworks (even React standalone) handle this shared state problem. Do you know any test of this kind that has been already made it?

@generia
Copy link

generia commented Sep 29, 2020

HI there,
for digging into Kea hands on I ported the Redux posts application example to work with Kea instead of Redux Toolkit, see https://github.com/generia/redux-essentials-example-app-with-kea.

The port was almost straight forward by converting the Redux Toolkit feature "slices" to Kea's "logic" components. The rest of the project is more or less a 1:1 copy.

As an overall personal impression in comparison to Redux Toolkit Kea helps you better to

  • write less boilerplate redux code
  • write custom abstractions
  • separate frontend and business logic
  • split business logic into modules

Especially I like the clean design of the overall framework. You can easily get the overall idea by just reading the framework code. Based on this, by using the generic plugin mechanism, it is comparably simple to enhance the framework with custom abstractions that fit your project specific needs.

@fantasticsoul
Copy link

Hey dude, Kea is cool, what do you think of concent?

@JasonStoltz
Copy link

JasonStoltz commented Dec 22, 2020

The only thing I have it to compare with is plain-old-redux. I would take Kea over Redux any day:

  • Everything in 1 logic file with much less boilerplate simplifies and makes things easier to reason about.
  • Having reducers grouped by individual state fields makes the expectations of actions and reducers much more apparent when reading a logic file.
  • The fact that individual stores are scoped within a component's mount and unmount lifecycle is huge. I don't have to worry about resetting global state on a product detail page, for instance, when a user navigates away. The store is automatically unmounted when the detail page is unmounted.
  • Selectors and thunks are first class concepts.
  • The fact that you can still use Redux dev tools is also very handy.
  • I generally just have to think less that I do when I use Redux.

@fantasticsoul
Copy link

fantasticsoul commented Dec 24, 2020

  • Everything in 1 logic file with much less boilerplate simplifies and makes things easier to reason about.

Making Everything in 1 logic file or one dir is really a good way to improve soft engineer's develop experience, look at the code snippet below that comes from concent

// logic.js
import { configure } from 'concent';

const delay = ()=> new Promise(r=> setTimeout(r, 1000));

const state = ()=> ({num:1, bigNum:100});

const reducer = {
  async initState(){
    await delay();
    return {num: 10,  bigNum: 1000};
  },
  inc(payload, moduleState){
    return {num: moduleState.num +1 };
  },
  async asyncInc(){
    await delay();
    return {num: moduleState.num +1 };
  },
};

const computed = {
  // only num change will trigger this computed fn
  doubleNum: ({num})=> num *2,
};

const lifecycle = {
  mounted: (dispatch) => dispatch("initState"), // [optional] triggered when the first ins of counter module mounted
  willUnmount: (dispatch) => dispatch("initState") // [optional] triggered when the last ins of counter module unmount
};

// configure them to a module named counterMod
configure('counterMod', {state, computed, reducer, lifecycle});

then you can use it in anywhere you want

// for class component
@register('counter')
class DemoCls extends React.Component{
  render(){
    const { state, mr, moduleComputed } = this.ctx;
    // here if read num, it means current ins render dep keys is ['num']
    return <button onClick={mr.inc}>{state.num} {moduleComputed.doubleNum}</button>
  }
}

// for function component
function DemoFn(){
  const { state, mr, moduleComputed } = useConcent('counter');
  return <button onClick={mr.inc}>{state.num} {moduleComputed.doubleNum}</button>
}

@mariusandra
Copy link
Member Author

@fantasticsoul that is a legit yet totally different approach from Kea. With kea the same code would look something like this:

const delay = () => new Promise((r) => setTimeout(r, 1000))
const logic = kea({
    actions: {
        setValues: (num, bigNum) => ({ num, bigNum }),
        inc: true,
        asyncInc: true,
    },
    defaults: {
        num: 1,
        bigNum: 100,
    },
    reducers: {
        num: {
            inc: (state) => state + 1,
            setValues: (_, { num }) => num,
        },
        bigNum: {
            setValues: (_, { bigNum }) => bigNum,
        },
    },
    listeners: ({ actions }) => ({
        asyncInc: async () => {
            await delay()
            actions.inc()
        },
    }),
    selectors: {
        doubleNum: [(s) => [s.num], (num) => num * 2],
    },
    effects: ({ actions }) => ({
        afterMount: async () => {
            await delay()
            actions.setValues(10, 1000)
        },
    }),
})

function DemoFn(){
  const { num, doubleNum } = useValues(logic);
  const { inc } = useActions(logic);
  return <button onClick={inc}>{num} {doubleNum}</button>
}

The example is very specific of course, and seems to be tailored to your library's features. Thus the kea code is definitely longer as I had to use many different features to get exactly the same functionality. I would argue it's still easier to understand though :).

Kea's approach is also easier for code splitting, as the slice of logic is always passed via ES imports (useValues(logic)) and not via magic strings (useConcent("counter")).

In any case, good luck with your library! :)

@Tbhesswebber
Copy link

I know that I'm a bit late to the game, but I just wanted to call out that, after a few hours of exploring Kea, I love it. The boilerplate of Redux typically made it so I had to weigh the value of time-travel debugging and determine if the complexity of the application would benefit from time-travel - rarely did I end up pulling in Redux. I generally stuck with pure React hooks that I would lift up into their own contexts as needed and when I did need complexity, foregoing flux in favor of state charts and state machines was the route that I often chose purely from a DX perspective. Kea is to Redux/flux what xState is to state machines - and I find it to be a total game-changer.

Thanks for the great React state framework and all of the surrounding tooling!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants