Mobx-state-tree: Computed properties in Redux

Created on 4 Sep 2017  路  10Comments  路  Source: mobxjs/mobx-state-tree

Is this possible to use computed properties with redux, so I don't have to use reselect?

All 10 comments

Not if you use redux connect(), but @mattiamanzati created this HOC to convert from a redux immutable (snapshot) back to a type: https://github.com/mobxjs/mobx-state-tree/issues/39#issuecomment-310662846

I've been thinking more about this problem since then, and I think a good way to solve it would be to expose a store.getStores() method on the store returned by asReduxStore so we can use:

// mobx-redux Provider:
<Provider {...store.getStores()} >
// Instead of react-redux Provider:
<Provider stores={store} >

and then use mobx-react inject() instead of redux connect() in our components.

If you just not want to use reselect, think about withProps

const computeFullName = ({ firstName, lastName }) => ({ name: firstName + lastName });

@withProps(computeFullName)
class SomeComponent extends Component{
  render() {
    const name = this.props.name;
    ...
  }
}

@luisherranz can i still use redux devtools if I use mobx-react?

Yeah, you should be able to keep using the connectReduxDevtools provided by MST.

But I've just realized you don't need a stoge.getStores() function because asReduxStore receives the initialized store.

You also have to add the dispatch to the Provider.

So I guess something like this should work:

import { Provider } from 'react-mobx' // <- not from react-redux

const mobxStores = Stores.create(initialState)
const reduxStore = asReduxStore(mobxStores)
connectReduxDevtools(require('remotedev'), reduxStore)

render(
    <Provider {...mobxStores} dispatch={reduxStore.dispatch}>
        <App />
    </Provider>,
    document.getElementById("root")
)

And then you could write components like this:

const Counter = ({ counter, increment }) => 
  <div>
    <div>{counter}</div>
    <button onClick={increment}>increment</button>
  </div>


export default inject(({ counter, dispatch }) => ({
    counter: counter.value,
    increment: () => dispatch({ type: 'INCREMENT' }),
  })
)(Counter)

Maybe the problem with this approach is that you cannot import external redux reducers from other libraries like redux-forms, apollo, redux-first-router and so on... But I guess that could be solved creating something like a reduxToMst model.

A question, I was thinking of using mobx-state-tree only for ui changes. Can I combine the Mobx store easily with my current Redux store ? If yes, how would you do it ?

As an example I was thinking of a search box that filters a list of contact by name. The search box term will be stored in a mobx-state-tree but the action to fetch new items will still be dispatched with a reducer.
Thanks !

I think something like this could work:
https://codesandbox.io/s/62m5jo6l5n

const MobxStore = types
  .model({
    number: 2
  })
  .actions(self => ({
    setNumber: number => (self.number = number)
  }));

const mobxStore = MobxStore.create({});

const mobxReducer = (state, action) => {
  switch (action.type) {
    case "MOBX_NUMBER_CHANGED":
    case "BOTH_NUMBERS_CHANGED":
      mobxStore.setNumber(action.number);
      break;
  }
  return getSnapshot(mobxStore);
};

I think getting a new snapshot each time an action is dispatched shouldn't be a problem:

Requesting a snapshot is cheap, as MST always maintains a snapshot of each node in the background, and uses structural sharing

Is that true @mweststrate @mattiamanzati?

Yep!

That's amazing ! It offers so many possibilities now. Thanks @mattiamanzati @luisherranz

I have refactored a bit your code @luisherranz, I just can't stand switch case in javascript, using redux-actions instead https://codesandbox.io/s/zq845xrp94 :)

Sure :)

Was this page helpful?
0 / 5 - 0 ratings