MithrilJS / mithril.js

A JavaScript Framework for Building Brilliant Applications
https://mithril.js.org
MIT License
14.02k stars 926 forks source link

State Management #2495

Closed hadihammurabi closed 3 years ago

hadihammurabi commented 5 years ago

How MithrilJS handle state of component?

osban commented 5 years ago

@hadihammurabi for internal component state you can use a closure, e.g.:

const closure = () => {
  let count = 0

  return {
    view: () =>
      m('div',
        m('h1', `Count: ${count}`),
        m('button', {onclick: () => count++}, "+"),
        m('button', {onclick: () => count--}, "-")
      )
  }
}
hadihammurabi commented 5 years ago

@osban How about global state?

orbitbot commented 5 years ago

@hadihammurabi Mithril does not really have a specific opinion of how to manage state in general, so it's up to you to do what makes the most sense to you.

So, the question becomes something like "how do I pass data to components", which is discussed in the docs using typical parameters here: https://mithril.js.org/components.html#passing-data-to-components - however, you can of course use any variable that is in scope, either being globally available in the window, or imported into the file that you're programming. Because of Mithril's autoredraw system, user interaction updates to variables will usually "just work", but if you have other asynchronous updates, you will probably need to use m.redraw here and there to make sure that the view is updated at the proper times.

Essentially, if you'd like to use any particular framwork like redux/saga/mobx/immer/flyd etc. it's mostly a question of exposing data to the components that you write, and depending on how the data is updated making sure that the views are also updated with m.redraw as required.

Mithril also has a streams-implementation that can be used unless you already have experience with or even an existing state management system in place https://mithril.js.org/stream.html - but depending on your actual case even this might be more than you actually need.

You might want to browse through some available code snippets in https://github.com/orbitbot/awesome-mithril and https://how-to-mithril.js.org , as well as read/skim through the documentation at https://mithril.js.org.

Drop by the chat at https://gitter.im/mithriljs/mithril.js if you have specific questions/problems, since this is a large topic and not really all that Mithril-specific outside of the integrations outlined above.

osban commented 5 years ago

@hadihammurabi when more components need access to the same state, global state can be used, which can be a simple POJO. Other options are also possible, as @orbitbot has explained.

foucist commented 2 years ago

I think this is @porsager's Mitosis, a tweak of @foxdonut's Meiosis State Management pattern. Just sharing this in here for posteriority.

const State = () => ({ count: 0 });

const Actions = state => ({
  increment: () => state.count += 1,
  decrement: () => state.count -= 1
});

const state   = State();
const actions = Actions(state);

const Counter = {
  view: ({ attrs: { state, actions } }) =>
    m('div',
      m('h1', 'Counter'),
      m('p', state.count),
      m('button', { onclick: actions.increment }, '+'),
      m('button', { onclick: actions.decrement }, '-')
    )
};

m.mount(document.body, {
  view: () => m(Counter, { state, actions })
});
up9cloud commented 1 month ago

I think this is @porsager's Mitosis, a tweak of @foxdonut's Meiosis State Management pattern. Just sharing this in here for posteriority.

const State = () => ({ count: 0 });

const Actions = state => ({
  increment: () => state.count += 1,
  decrement: () => state.count -= 1
});

const state   = State();
const actions = Actions(state);

const Counter = {
  view: ({ attrs: { state, actions } }) =>
    m('div',
      m('h1', 'Counter'),
      m('p', state.count),
      m('button', { onclick: actions.increment }, '+'),
      m('button', { onclick: actions.decrement }, '-')
    )
};

m.mount(document.body, {
  view: () => m(Counter, { state, actions })
});

Thanks a lot!

Here is my JSX version for anyone who's stuck like I was

export const state   = { count: 0 }
export const actions = {
  increment: () => state.count += 1,
  decrement: () => state.count -= 1
}
export const Counter = {
  view: () => {
    return (
      <div>
        <div>{state.count}</div>
        <button onclick={actions.increment}>+</button>
        <button onclick={actions.decrement}>-</button>
      </div>
    )
  }
}
m.mount(document.getElementById("app"), {
  view: () => <Counter></Counter>
})