erik-sn
erik-sn

Reputation: 2600

React/Redux, implementing multiple actions with Redux Thunk

I am learning a react/redux and have an application with two main pieces of state:

  1. An array of items
  2. An object that contains user-specified filters for those items

I have three functions/actions, createFilter, updateFilter, and deleteFilter that modify the state of #2. I have an action filterItems that modifies #1 based on the state of #2. So whenever #2 changes, this action needs to be dispatched.

This is the component I am working with:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'

import { createFilter } from '../actions/actions'
import { updateFilter } from '../actions/actions'
import { deleteFilter } from '../actions/actions'
import { filterItems } from '../actions/actions'

class ItemList extends Component {

 createFilter(input) {
       this.props.createFilter(input)
       this.props.filterItems()
    }

    updateFilter(input) {
       this.props.updateFilter(input)
       this.props.filterItems()
    }

    deleteFilter() {
       this.props.deleteFilter()
       this.props.filterItems()
    }

    ...
    // Render method
    ...
}

function mapDispatchToProps(dispatch) {
    return bindActionCreators({ createFilter, updateFilter, deleteFilter, filterItems }, dispatch)
}

function mapStateToProps({ itemList }) {
    return { itemList }
}

export default connect(mapStateToProps, mapDispatchToProps)(ItemList)

What I have found is that when one of the filter methods are sent, the store (state #2) is not yet updated by the time filterItems() is called.

So I need to asynchronously execute the filter functions, and once the store is updated call filterItems.

I am struggling on how to do this with react-thunk. If the first function was an ajax promise I would use .then():

export function updateFilterAndEvaluate(input) {
    return (dispatch, getState) => {
        updateFilter(input).then(dispatch(filterItems(getState().filters)))
    }
}

But these are just functions, and don't have a .then() method. I am trying to figure out what my best course of action is for this implementation. Can I wrap Redux actions in a promise? Am I misusing Thunk? Or should I attempt a different pattern entirely?

Upvotes: 0

Views: 2934

Answers (1)

Dan Abramov
Dan Abramov

Reputation: 268293

I have an action filterItems that modifies #1 based on the state of #2.

This is, generally speaking, an anti-pattern. Since the result array can be computed from the source array and the currently active filters, you shouldn’t be keeping it in the state.

Redux actions should generally look like “events” (e.g. what happened). “Filter was created” and “filter was updated” are good actions. “Filter them now!” looks more like a command, this is usually a sign that it shouldn’t have been an action in the first place, and should be something the components do as they select the data to render.

Instead, do the filtering as part of your mapStateToProps() function when you prepare data for the components. If it gets expensive, look into using Reselect to compute derived data efficiently.

As for your specific question,

What I have found is that when one of the filter methods are sent, the store (state #2) is not yet updated by the time filterItems() is called.

This is incorrect and indicates some other problem in your code. (It’s hard to tell where because the example is incomplete). In Redux, dispatch() is synchronous (unless you have some middleware that delays or batches it which usually isn’t the case), so you don’t need to “wait” for it to finish if it just operates on the local data.

However, in any case, filterItems() is not a very good fit for an action, and I suggest you to look into filtering in mapStateToProps() as I wrote above.

Upvotes: 6

Related Questions