Tag Archives: Redux

On MobX Redux dilemma

In my experiences using MobX as a state management library produces less code and lets you get things done quicker (two-folded knife!) but with Redux you get a lot of stuff for free – most notable is scalable approach to architecture and maintenance of web applications.

I personally prefer Redux also because it’s functional like React which puts a lot of emphasize on functional stateless components and it makes me feel like a JavaScript developer. While that means it’s more verbose, it contains minimal abstraction and thus is easier to debug.

The best way of rendering a collection of items in React/Redux

In short

The best way to render a collection in React, backed by Redux or other similar library: store a collection as an object where key is item id and value is item itself. That way when rendering collection, pass only item’s id to the item component. Than in item’s connected component query a collection by item’s id you passed early. So something like this.

//Item.js
import React from 'react'
import {connect} from 'react-redux'

const Item = ({item}) => {
    // use item to render markup
}

const mapStateToProps = state, {id} => ({
    item: state.collection.data[id]
})

export default connect(mapStateToProps)(Item)
// Collection.js
import React from 'react'
import {connect} from 'react-redux'
import Item from './Item'

const Collection = ({ids}) => (
    <div>{ids.map(id => <Item key={id} id={id} />)}</div>
)

const mapStateToProps = state => ({
    ids: state.collection.ids // actual collection is obtained with state.collection.data 
})

export default connect(mapStateToProps)(Collection)

Storing collection as an object also improves performance of querying and deleting an item from it.

Longer explanation

When I began developing in React a few years ago I was rendering a collection of items this way:

items.map(item => <Item {...item} key={item.id} />)

Then Flux/Redux came in my way and I realized storing items in state as a list of items is inefficient as noted. That’s because querying a list is O(n) operation but querying an object only O(1).

Given that performance boost I was still rendering a collection as before only now I had to turn an object into a ordered list because .map() only works on lists. Imagine doing that after every render! Using reselect can improve this by only converting it to list when collection changes. But we can still do better than that.

Satisfying solution came after reading a blog posts about making React and Redux more performant together. The point I think was “don’t be afraid of using connect often and breaking reducer into smaller reducers”.

You can build object of (id, item) pairs yourself or use library like normalizr which normalizes input data according to provided schema and returns entities and result (ids).

The reason why I prefer this method over previous is also because I love to use functional programming in react/redux. I think those two are brilliant couple. But to make functional programming easier, I use utility libraries. The two most known are lodash/fp and ramda. Lodash/fp might be superior to ramda when it comes to amount of functions included but I love ramda more because of the documentation. It’s easier to find what you are looking for (in lodash/fp I need to look at two places).

I was indecisive though which one to pick and stick with before discovering this new way of rendering collection because ramda doesn’t support iteration over an object.

How to reduce amount of dispatched actions in Redux

When I was beginner in using Redux to manage state in JavaScript while using React, I often dispatched unnecessary amount of actions when I could be done with just one or two. Or when I was loading initial data when user visits website, I often dispatched an action that set only specific resource. Something like this:

const fetchAppData = () => dispatch => apiClient.fetchAppData()
    .then(res => res.json())
    .then(res => {
        if (res.error) {
            dispatch(actions.showNotification(res, false))
            return
        }
        dispatch(actions.setUser(res.users)) // action type is SET_USER
        dispatch(actions.setPosts(res.posts)) // action type is SET_POSTS
    })

I eventually figured it out Redux store can handle one action in multiple reducers at the same time so the two dispatch lines became one dispatch(actions.setAppData(res)). Action type is SET_APP_DATA and is defined as case in both reducers as opposed to SET_USER which is defined only in users reducer and SET_POSTS only in posts reducer.

Another use case when you can reduce amount of dispatched actions is when you set a data and at the same time you want to display a notification. Following the previous example we could have another reducer for notifications that sets state of notification to {type:'SUCCESS', message: 'Data loaded'} when SET_APP_DATA is dispatched.

Using Ramda’s evolve in Redux reducers to create new state

Ramda is a JavaScript utility library similar to lodash or underscore ((I haven’t used underscore in ages though)) with an extra touch – it’s designed to be better suited to functional style of programming. For example it provides automatic currying ((Remodeling function in a way that instead of accepting multiple arguments at once it accepts them one by one. Useful when pre-loading with known arguments or when composing functions that shares inputs and outputs – output of previous function is input to the next one.)) for all of its functions. But what I like most about Ramda is that it never mutates user data. Which makes it super well suited for applications that heavily relies on immutability (in my case applications that use Redux for managing their state). And in a process it makes code cleaner.

Take a look at the following example. It’s a reducer that adds, removes a group of contacts and adds, remove contacts from group. Every group has a property contactIds that ties contacts from other part of the store to particular group. When group is removed, its contacts are transferred to default group. I’m also using normalizr library when adding groups to keep groups in store IDed (not the point of this post so just play along if you don’t know about this library).

export default (state={}, action={}) => {
  const {type, payload} = action
  switch(type) {
    case REMOVE_GROUP:
      let groups = filter(state, (g, id) => id != payload.id)
      groups[0].contactIds.push(state[payload.id].contactIds)
      return groups
    case ADD_GROUPS:
      const data = normalize(payload.groups, [new schema.Entity('groups')])
      return Object.assign({}, state, data.entities.groups)
    case ADD_CONTACT_TO_GROUP:
      groups = {...state}
      groups[payload.groupId].contactIds.push(payload.contactId)
      return groups
    case REMOVE_CONTACT_FROM_GROUP:
      groups = {...state}
      groups[payload.groupId].contactIds = filter(groups[payload.groupId].contactIds, id => id != payload.contactId)
      return groups
    default: return state
  }
}

I’m fine with code in first two cases, but the last two cases could use an improvement so that I wouldn’t have to clone the state object and then specify same nested contactsId twice.

If you are familiar with Immutable than you’d probably think of its setIn and than as second parameter provide a updater function which gives you previous property’s value to operate with. You also need to convert state object to a Immutable data structure (Map, List, Record) beforehand so you can use that setIn. Not to mention sometimes you need to convert back and forth to plain JavaScript objects when using outside of reducers. Man, that extra work really doesn’t make me enthusiastic about Immutable (even if it’s written and managed by Facebook).

Equivalent in Ramda is using evolve. Evolve takes two parameters. First is transformation object that mimics our state object but only contains properties that we want to update/evolve. Their values are transformation functions that takes value of that property as an input. Second parameter to evolve is our data to be transformed.

Below you can find refactored snippet using evolve. There wasn’t a need for any custom-made transformation functions because one adds an id to list, other removes it and there are Ramda’s append and without for that.

export default (state={}, action={}) => {
  const {type, payload} = action
  switch(type) {
    //...
    case ADD_CONTACT_TO_GROUP:
      return R.evolve({
        [payload.groupId]: {
          contactIds: R.append(payload.contactId)
        }
      }, state)
    case REMOVE_CONTACT_FROM_GROUP:
      return R.evolve({
        [payload.groupId]: {
          contactIds: R.without(R.of(payload.contactId))
        }
      }, state)
    //...
  }
}