How to setup Ember like computed properties in Immutablejs and Redux and Flux and React
Asked Answered
I

4

9

I am used to computed properties in Ember Object Model. It's a convenient way to specify computed properties that depend on other properties.

Say fullName depends on firstName and lastName, I can setup computed properties as a function computeProperties and call computeProperties each time I make a change.

Example:

function computeFullName(state) {
  const fullName = state.get('firstName') + state.get('lastName');
  const nextState = state.set('fullName', fullName);
  return nextState;
}

function computeProperties(state) {
  const nextState = computeFullName(state);
  return nextState;
}

// store action handler
[handleActionX](state) {

  let nextState = state.set('firstName', 'John');
  nextState = state.set('lastName', 'Doe');

  nextState = computeProperties(nextState);

  return nextState;
}

Is there a way to automatically setup computed properties so that I don't have to call extra functions each time. In Redux or in ImmutableJS.

Invaluable answered 23/7, 2015 at 8:7 Comment(0)
E
11

Redux author here!

Using reselect as suggested by WildService is the way to go. I think we won't include this in core because reselect does its job well and we're fine with it being a separate library.

I wanted to note a couple of things:

  • Even with reselect, you don't want to compute data inside your reducer. Selectors should operate on the state managed by reducers. In other words, selectors are the step between your Redux store state and your components—they are not inside your reducers. It is essential you keep Redux state normalized so it's easy to update.

  • We actually encourage you to define selectors alongside the relevant reducers, so that when you change the state shape, you don't have to change your components—they would be using the selectors instead. You can see an example of this in the Redux folder of Flux Comparison

  • We have a documentation page introducing reselect and describing how to use it for computing derived data. Check it out.

Esquibel answered 3/10, 2015 at 12:17 Comment(0)
B
4

Check out reselect. Composable pure functions for efficiently computing derived data from stores. Afaik there are plans to roll reselect's selectors into Redux core at some stage if they prove popular. There's an example for usage with ImmutableJS at the bottom of the readme too.

Baccarat answered 2/8, 2015 at 13:50 Comment(0)
P
1

To create computed properties you can use the standalone observable library mobservable.

var user = mobservable.props({
  firstName: 'John',
  lastName: 'Doe',
  fullName: function() {
    return this.firstName + this.lastName
  }
});

var nameViewer = mobservable.ObservingComponent(React.createClass({
   render: function() {
       return (<span>{user.fullName}</span>)
   }
});

That should be the gist of it, now any change to user.firstName or lastName will rerender your nameViewer component. You can further combine this with flux implementations like redux to change the data, and push the user itself through your component tree. But note that the user object itself is not immutable (in that case it wouldn't be observable after all ;-)) Also see this trivial and slightly more interesting fiddles for some examples.

Phillipphillipe answered 23/7, 2015 at 18:46 Comment(4)
I need a solution including Immutable libraryInvaluable
In that case I think you have to manually encode such logic in your stores, or, which is more the react way, as (utility) functions that are used from your components, based on the real data, so that during each render the value is updated.Phillipphillipe
should I keep the computed state in stores or in react component state? @PhillipphillipeInvaluable
the philosophy is to not store computed state if possible :) otherwise it depends on the amount of reuse I think, if the computations aren't reused by other components I would put it in the component state, otherwise in stores.Phillipphillipe
E
0

What about something like this?

export const getWidgetsWithComputedProps = widgets => {
  return widgets.map(w => getWidgetWithComputedProps(w));
};

export const selectWidgetType = widget => {
  switch (widget.type) {
    case 'line':
      return 'time-series';
    case 'pie':
    case 'bar':
      return 'cross-sectional';
    default:
      console.warn('Back up: that type of widget does not exist!', widget.type);
      return null;
  }
};

export const getWidgetWithComputedProps = createSelector(
  widget => widget,
  selectWidgetType,
  (widget, _type) => {
    return {...widget, _type}
  }
);
Eichler answered 31/10, 2016 at 9:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.