Matt Saunders
Matt Saunders

Reputation: 4391

React Redux Thunk trigger multiple actions on one call

I have an action which in turn must affect many other areas of my app state. In this case, when the user selects a website from a dropdown list, it must update many other components. I'm currently doing it like so:

setSelectedWebsite (websiteId) {
    // The core reason for this component
    this.props.setSelectedWebsite(websiteId);

    // Fetch all associated accounts
    this.props.fetchAllAccounts(websiteId)

    // Several other "side effect" calls here...
}

In this interest of making one component serve one purpose, this feels like a bad practice.

What is the best practice for triggering multiple actions in one call from a component?

Upvotes: 1

Views: 1354

Answers (2)

Eduard
Eduard

Reputation: 9205

You could use redux-thunk.

Your component's method:

setSelectedWebsite(websiteId){
    this.props.handleSetSelectedWebsite(websiteId) // this is a thunk
}

Your Redux file with action creators / thunks:

// this function is a thunk

export const handleSetSelectedWebsite = (websiteId) => (dispatch) => {
    dispatch(setSelectedWebsite(websiteId))
    dispatch(fetchAllAccounts(websiteId))
}

// these function are action creators (or could be other thunks if you style them the same way as the thunk above)

const setSelectedWebsite = (websiteId) => {
    // your code
}

const fetchAllAccounts = (websiteId) => {
    // your code
}

Upvotes: 3

syllabix
syllabix

Reputation: 2295

For handling complex side effects in a redux application, I would recommend looking at using Redux Sagas. I have seen its usage grow in popularity on projects large and small, and for good reason.

With sagas, in the example you have provided, you can emit a single action from a function provided through mapDispatchToProps and let a saga take care of the rest. For example: (following example assumes flux standard actions)

//import redux connect, react, etc

class SiteSelector extends React.Component {

    render() {
        const id = this.props.id;

        return (
           <button onClick={ () => this.props.action(id)>Click Me</button>
        )
    }
}

const mapStateToProps = (state) => ({
    id: state.websiteId
})

const mapDispatchToProps = dispatch => ({
    action: (id) => dispatch(setSelectedWebsite(id))
})

export connect(mapStateToProps, mapDispatchToProps)(SiteSelector)

Now you can handle the action emitted from setSelectedWebsite in a saga like so:

//import all saga dependencies, etc 

export function* selectSite(action) {
  const id = action.payload.id
  yield put(Actions.selectWebsite(id))
  const results = yield call(Api.fetchAllAccounts, id)
  yield //do something complex with results
  yield //do something else...
  yield //and keep going...
}

// Our watcher Saga: spawn a new selectSite task every time the action from setSelectedWebsite is dispatched 
export function* watchForSiteSelection() {
  yield takeEvery('SITE_SELECTED_ACTION', selectSite)
}

For reference checkout the docs: Redux Sagas

Upvotes: 0

Related Questions