Search code examples
reactjsreduxreact-reduxredux-thunk

how and when to call a react component methods after state change from redux


In a single react component a user clicks a button => which calls a method => triggers an action => async fetch => reducer updates state => component receives new props.

back in the original component that triggered the action I have been using:

componentWillReceiveProps(nextProps){
    if(nextProps.someProp !== this.props.someProp){
        //ok new prop is here
        this.someMethod(nextProps.someProp);
    }
}

Am I going about this in the right way?

It just seems somewhat clunky and detached as a callback mechanism from the user action or change of state. It just makes following the logical flow of the component harder once there are a few of these, I have a component with 3 of these and already think it's not as easy to reason about especially when they are part of a related flow a > b > c . And I have ended up with this kind of thing:

componentWillReceiveProps(nextProps){

    if(this.patchJavaScriptWillLoad(nextProps)){
        this.createPatchInstance();
        // method fires an action which will also result in state change that triggers the below.
    }

    if(this.patchInstanceWillBeReady(nextProps)){
        this.startPatchAudio(nextProps.webAudioPatch.instance);
        // method fires an action which will also result in state change that triggers the below.
    }

    if(this.patchParametersWillChange(nextProps)){
        this.updateWebAudioPatchParameters(nextProps.webAudioPatchParameters);
    }
}

// abstracted away if conditions to make componentWillReceiveProps more readable. 

But is this how it should be done or is this a symptom of not moving enough logic to action creators?


Solution

  • More detailed examples would be useful, but based on what you have here, I think I see what you're getting at.

    Short answer: Yes, this is a symptom of not moving enough logic to the action creators. Ideally, your component should be a pure view component. No need for componentWillReceiveProps in most cases - you simply render whatever the props are and that's that. This is why Abramov (creator of redux) argues for functional components. More on that here.

    If you need to perform other actions after an async call returns with some data, you would do that, as you said, in the action creator. I'm going to go with an example that uses thunks:

    EDIT: I added an example of the component passing a reference to the audio player as an argument to the action. That way, the action can manipulate after the async step.

    //An async action creator that uses the thunk pattern.
    //You could call this method from your component just like any other
    //action creator.
    
    export function getMaDatums(audioPlayer, audioContext) {
      return function (dispatch) {
    
        //make the actual call to get the data
        return fetch(`http://<your stuff here>`)
          .then(data => {
    
            //call dispatch again to do stuff with the data
            dispatch(gotDataAction(data));
    
            //call dispatch some more to take further actions
            dispatch(...);
    
            //since the component passed us references to these, we can
            //interact with them here, after our data has loaded! FTW!
            audioPlayer.doTheThings();
            audioSession.doTheOtherThings();
    
            //plus anything else you want...
          });
      }
    }
    

    If you want to learn more about the doing async stuff with redux, or for that matter, interfacing with stateful libraries from your redux app, I highly recommend giving the redux documentation a good read. The basis of the thunk example above came from here.

    Good luck and have fun with React + Redux!