Redux store not up to date in component did update












0















I have a React container for user login. When it mounts it fetches some third-party scripts and injects them into the page (this creates a login widget)



The login phase is in two parts, the first part is to log in to the third party system, on completion of that I then need to login to a second system using the payload from the first.



On componentDidMount I fire a request to fetch the third party scripts, all is good here.



When I click on the login button (its a pre-built widget provided by the third-party that fires a callback we can hook into, no need to create the widget etc itself), the callback fires and we get a customer token back, again all good here.



The part that I'm having trouble with using that customer token, I use redux to add it to the store, inside componentDidUpdate I then check prevProps against current props to check for the token, if it exists I fire an action to log in to the second system. I wanted to avoid this action firing multiple times so once its fired once I add a hasRequested field to the store and then check against this in componentDidUpdate to safeguard against it getting called multiple times.



This bit doesn't work, because I have multiple instances of the LoginContainer on the page, the componentDidUpdate is firing multiple times and the store isn't getting updated in time with the hasRequested value, the outcome being that I end up making multiple API requests.



Anyone got any ideas how I can have multiple instances of a container but limit the number of times an action fires when its props update?



Sample code below



LoginContainer



class LoginContainer extends Component {
/**
* componentDidMount
*/
componentDidMount () {
if (!this.props.thirdPartyCode.hasRequested) {
this.props.fetchThirdPartySriptsAndLoad();
}
}

/**
* componentDidUpdate
*/
componentDidUpdate (prevProps) {
if (
prevProps.authentication.customerToken !== this.props.authentication.customerToken
&& !this.props.authentication.hasRequested
) {
this.props.authenticateUserWithSecondSystem();
}
}


Authentication reducer



import {
SECOND_SYSTEM_AUTHENTICATE_REQUEST,
SECOND_SYSTEM_AUTHENTICATE_SUCCESS,
SECOND_SYSTEM_AUTHENTICATE_FAILURE,
} from 'actions/authentication/authentication-actions';

export const defaultState = {
isFetching: false,
hasRequested: false,
error: '',
customerToken: ''
};

/**
* Reducer for authentication
* @param {Object} state
* @param {Object} action
* @returns {Object} updated state
*/
export default function (state = defaultState, action) {
switch (action.type) {
case SECOND_SYSTEM_AUTHENTICATE_REQUEST:
return Object.assign({}, state, {
isFetching: true,
hasRequested: true
});
case SECOND_SYSTEM_AUTHENTICATE_SUCCESS:
return Object.assign({}, state, {
isFetching: false
});


Authentication actions



export const SECOND_SYSTEM_AUTHENTICATE_REQUEST = 'SECOND_SYSTEM_AUTHENTICATE_REQUEST';
export const SECOND_SYSTEM_AUTHENTICATE_SUCCESS = 'SECOND_SYSTEM_AUTHENTICATE_SUCCESS';
export const SECOND_SYSTEM_AUTHENTICATE_FAILURE = 'SECOND_SYSTEM_AUTHENTICATE_FAILURE';

/**
* Request Authenticate
* @returns {{type: string}}
*/
function requestSecondSystemAuthentication () {
return {
type: SECOND_SYSTEM_AUTHENTICATE_REQUEST
};
}

/**
* Authenticate Success
* @param {Object} data - response data
* @returns {{type: string}}
*/
function secondSystemAuthenticateSuccess (data) {
return {
type: SECOND_SYSTEM_AUTHENTICATE_SUCCESS,
data
};
}

/**
* Post Authenticate
* @returns {Function} - action creator
*/
export function authenticateUserWithSecondSystem (authenticationToken) {
return dispatch => {
dispatch(requestSecondSystemAuthentication());

ApiService.post('/api/authenticate', authenticationToken)
.then(payload => dispatch(secondSystemAuthenticateSuccess(payload)))
}









share|improve this question





























    0















    I have a React container for user login. When it mounts it fetches some third-party scripts and injects them into the page (this creates a login widget)



    The login phase is in two parts, the first part is to log in to the third party system, on completion of that I then need to login to a second system using the payload from the first.



    On componentDidMount I fire a request to fetch the third party scripts, all is good here.



    When I click on the login button (its a pre-built widget provided by the third-party that fires a callback we can hook into, no need to create the widget etc itself), the callback fires and we get a customer token back, again all good here.



    The part that I'm having trouble with using that customer token, I use redux to add it to the store, inside componentDidUpdate I then check prevProps against current props to check for the token, if it exists I fire an action to log in to the second system. I wanted to avoid this action firing multiple times so once its fired once I add a hasRequested field to the store and then check against this in componentDidUpdate to safeguard against it getting called multiple times.



    This bit doesn't work, because I have multiple instances of the LoginContainer on the page, the componentDidUpdate is firing multiple times and the store isn't getting updated in time with the hasRequested value, the outcome being that I end up making multiple API requests.



    Anyone got any ideas how I can have multiple instances of a container but limit the number of times an action fires when its props update?



    Sample code below



    LoginContainer



    class LoginContainer extends Component {
    /**
    * componentDidMount
    */
    componentDidMount () {
    if (!this.props.thirdPartyCode.hasRequested) {
    this.props.fetchThirdPartySriptsAndLoad();
    }
    }

    /**
    * componentDidUpdate
    */
    componentDidUpdate (prevProps) {
    if (
    prevProps.authentication.customerToken !== this.props.authentication.customerToken
    && !this.props.authentication.hasRequested
    ) {
    this.props.authenticateUserWithSecondSystem();
    }
    }


    Authentication reducer



    import {
    SECOND_SYSTEM_AUTHENTICATE_REQUEST,
    SECOND_SYSTEM_AUTHENTICATE_SUCCESS,
    SECOND_SYSTEM_AUTHENTICATE_FAILURE,
    } from 'actions/authentication/authentication-actions';

    export const defaultState = {
    isFetching: false,
    hasRequested: false,
    error: '',
    customerToken: ''
    };

    /**
    * Reducer for authentication
    * @param {Object} state
    * @param {Object} action
    * @returns {Object} updated state
    */
    export default function (state = defaultState, action) {
    switch (action.type) {
    case SECOND_SYSTEM_AUTHENTICATE_REQUEST:
    return Object.assign({}, state, {
    isFetching: true,
    hasRequested: true
    });
    case SECOND_SYSTEM_AUTHENTICATE_SUCCESS:
    return Object.assign({}, state, {
    isFetching: false
    });


    Authentication actions



    export const SECOND_SYSTEM_AUTHENTICATE_REQUEST = 'SECOND_SYSTEM_AUTHENTICATE_REQUEST';
    export const SECOND_SYSTEM_AUTHENTICATE_SUCCESS = 'SECOND_SYSTEM_AUTHENTICATE_SUCCESS';
    export const SECOND_SYSTEM_AUTHENTICATE_FAILURE = 'SECOND_SYSTEM_AUTHENTICATE_FAILURE';

    /**
    * Request Authenticate
    * @returns {{type: string}}
    */
    function requestSecondSystemAuthentication () {
    return {
    type: SECOND_SYSTEM_AUTHENTICATE_REQUEST
    };
    }

    /**
    * Authenticate Success
    * @param {Object} data - response data
    * @returns {{type: string}}
    */
    function secondSystemAuthenticateSuccess (data) {
    return {
    type: SECOND_SYSTEM_AUTHENTICATE_SUCCESS,
    data
    };
    }

    /**
    * Post Authenticate
    * @returns {Function} - action creator
    */
    export function authenticateUserWithSecondSystem (authenticationToken) {
    return dispatch => {
    dispatch(requestSecondSystemAuthentication());

    ApiService.post('/api/authenticate', authenticationToken)
    .then(payload => dispatch(secondSystemAuthenticateSuccess(payload)))
    }









    share|improve this question



























      0












      0








      0








      I have a React container for user login. When it mounts it fetches some third-party scripts and injects them into the page (this creates a login widget)



      The login phase is in two parts, the first part is to log in to the third party system, on completion of that I then need to login to a second system using the payload from the first.



      On componentDidMount I fire a request to fetch the third party scripts, all is good here.



      When I click on the login button (its a pre-built widget provided by the third-party that fires a callback we can hook into, no need to create the widget etc itself), the callback fires and we get a customer token back, again all good here.



      The part that I'm having trouble with using that customer token, I use redux to add it to the store, inside componentDidUpdate I then check prevProps against current props to check for the token, if it exists I fire an action to log in to the second system. I wanted to avoid this action firing multiple times so once its fired once I add a hasRequested field to the store and then check against this in componentDidUpdate to safeguard against it getting called multiple times.



      This bit doesn't work, because I have multiple instances of the LoginContainer on the page, the componentDidUpdate is firing multiple times and the store isn't getting updated in time with the hasRequested value, the outcome being that I end up making multiple API requests.



      Anyone got any ideas how I can have multiple instances of a container but limit the number of times an action fires when its props update?



      Sample code below



      LoginContainer



      class LoginContainer extends Component {
      /**
      * componentDidMount
      */
      componentDidMount () {
      if (!this.props.thirdPartyCode.hasRequested) {
      this.props.fetchThirdPartySriptsAndLoad();
      }
      }

      /**
      * componentDidUpdate
      */
      componentDidUpdate (prevProps) {
      if (
      prevProps.authentication.customerToken !== this.props.authentication.customerToken
      && !this.props.authentication.hasRequested
      ) {
      this.props.authenticateUserWithSecondSystem();
      }
      }


      Authentication reducer



      import {
      SECOND_SYSTEM_AUTHENTICATE_REQUEST,
      SECOND_SYSTEM_AUTHENTICATE_SUCCESS,
      SECOND_SYSTEM_AUTHENTICATE_FAILURE,
      } from 'actions/authentication/authentication-actions';

      export const defaultState = {
      isFetching: false,
      hasRequested: false,
      error: '',
      customerToken: ''
      };

      /**
      * Reducer for authentication
      * @param {Object} state
      * @param {Object} action
      * @returns {Object} updated state
      */
      export default function (state = defaultState, action) {
      switch (action.type) {
      case SECOND_SYSTEM_AUTHENTICATE_REQUEST:
      return Object.assign({}, state, {
      isFetching: true,
      hasRequested: true
      });
      case SECOND_SYSTEM_AUTHENTICATE_SUCCESS:
      return Object.assign({}, state, {
      isFetching: false
      });


      Authentication actions



      export const SECOND_SYSTEM_AUTHENTICATE_REQUEST = 'SECOND_SYSTEM_AUTHENTICATE_REQUEST';
      export const SECOND_SYSTEM_AUTHENTICATE_SUCCESS = 'SECOND_SYSTEM_AUTHENTICATE_SUCCESS';
      export const SECOND_SYSTEM_AUTHENTICATE_FAILURE = 'SECOND_SYSTEM_AUTHENTICATE_FAILURE';

      /**
      * Request Authenticate
      * @returns {{type: string}}
      */
      function requestSecondSystemAuthentication () {
      return {
      type: SECOND_SYSTEM_AUTHENTICATE_REQUEST
      };
      }

      /**
      * Authenticate Success
      * @param {Object} data - response data
      * @returns {{type: string}}
      */
      function secondSystemAuthenticateSuccess (data) {
      return {
      type: SECOND_SYSTEM_AUTHENTICATE_SUCCESS,
      data
      };
      }

      /**
      * Post Authenticate
      * @returns {Function} - action creator
      */
      export function authenticateUserWithSecondSystem (authenticationToken) {
      return dispatch => {
      dispatch(requestSecondSystemAuthentication());

      ApiService.post('/api/authenticate', authenticationToken)
      .then(payload => dispatch(secondSystemAuthenticateSuccess(payload)))
      }









      share|improve this question
















      I have a React container for user login. When it mounts it fetches some third-party scripts and injects them into the page (this creates a login widget)



      The login phase is in two parts, the first part is to log in to the third party system, on completion of that I then need to login to a second system using the payload from the first.



      On componentDidMount I fire a request to fetch the third party scripts, all is good here.



      When I click on the login button (its a pre-built widget provided by the third-party that fires a callback we can hook into, no need to create the widget etc itself), the callback fires and we get a customer token back, again all good here.



      The part that I'm having trouble with using that customer token, I use redux to add it to the store, inside componentDidUpdate I then check prevProps against current props to check for the token, if it exists I fire an action to log in to the second system. I wanted to avoid this action firing multiple times so once its fired once I add a hasRequested field to the store and then check against this in componentDidUpdate to safeguard against it getting called multiple times.



      This bit doesn't work, because I have multiple instances of the LoginContainer on the page, the componentDidUpdate is firing multiple times and the store isn't getting updated in time with the hasRequested value, the outcome being that I end up making multiple API requests.



      Anyone got any ideas how I can have multiple instances of a container but limit the number of times an action fires when its props update?



      Sample code below



      LoginContainer



      class LoginContainer extends Component {
      /**
      * componentDidMount
      */
      componentDidMount () {
      if (!this.props.thirdPartyCode.hasRequested) {
      this.props.fetchThirdPartySriptsAndLoad();
      }
      }

      /**
      * componentDidUpdate
      */
      componentDidUpdate (prevProps) {
      if (
      prevProps.authentication.customerToken !== this.props.authentication.customerToken
      && !this.props.authentication.hasRequested
      ) {
      this.props.authenticateUserWithSecondSystem();
      }
      }


      Authentication reducer



      import {
      SECOND_SYSTEM_AUTHENTICATE_REQUEST,
      SECOND_SYSTEM_AUTHENTICATE_SUCCESS,
      SECOND_SYSTEM_AUTHENTICATE_FAILURE,
      } from 'actions/authentication/authentication-actions';

      export const defaultState = {
      isFetching: false,
      hasRequested: false,
      error: '',
      customerToken: ''
      };

      /**
      * Reducer for authentication
      * @param {Object} state
      * @param {Object} action
      * @returns {Object} updated state
      */
      export default function (state = defaultState, action) {
      switch (action.type) {
      case SECOND_SYSTEM_AUTHENTICATE_REQUEST:
      return Object.assign({}, state, {
      isFetching: true,
      hasRequested: true
      });
      case SECOND_SYSTEM_AUTHENTICATE_SUCCESS:
      return Object.assign({}, state, {
      isFetching: false
      });


      Authentication actions



      export const SECOND_SYSTEM_AUTHENTICATE_REQUEST = 'SECOND_SYSTEM_AUTHENTICATE_REQUEST';
      export const SECOND_SYSTEM_AUTHENTICATE_SUCCESS = 'SECOND_SYSTEM_AUTHENTICATE_SUCCESS';
      export const SECOND_SYSTEM_AUTHENTICATE_FAILURE = 'SECOND_SYSTEM_AUTHENTICATE_FAILURE';

      /**
      * Request Authenticate
      * @returns {{type: string}}
      */
      function requestSecondSystemAuthentication () {
      return {
      type: SECOND_SYSTEM_AUTHENTICATE_REQUEST
      };
      }

      /**
      * Authenticate Success
      * @param {Object} data - response data
      * @returns {{type: string}}
      */
      function secondSystemAuthenticateSuccess (data) {
      return {
      type: SECOND_SYSTEM_AUTHENTICATE_SUCCESS,
      data
      };
      }

      /**
      * Post Authenticate
      * @returns {Function} - action creator
      */
      export function authenticateUserWithSecondSystem (authenticationToken) {
      return dispatch => {
      dispatch(requestSecondSystemAuthentication());

      ApiService.post('/api/authenticate', authenticationToken)
      .then(payload => dispatch(secondSystemAuthenticateSuccess(payload)))
      }






      javascript reactjs redux redux-thunk






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 15 '18 at 16:39







      woolm110

















      asked Nov 15 '18 at 15:48









      woolm110woolm110

      6831023




      6831023
























          1 Answer
          1






          active

          oldest

          votes


















          0














          In the end I decided I was trying to make it too complex, I've opted to have one action called authenticateUser that gets called on click and that handles the logic to login to the first provider and then the second.






          share|improve this answer

























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53323104%2fredux-store-not-up-to-date-in-component-did-update%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            In the end I decided I was trying to make it too complex, I've opted to have one action called authenticateUser that gets called on click and that handles the logic to login to the first provider and then the second.






            share|improve this answer






























              0














              In the end I decided I was trying to make it too complex, I've opted to have one action called authenticateUser that gets called on click and that handles the logic to login to the first provider and then the second.






              share|improve this answer




























                0












                0








                0







                In the end I decided I was trying to make it too complex, I've opted to have one action called authenticateUser that gets called on click and that handles the logic to login to the first provider and then the second.






                share|improve this answer















                In the end I decided I was trying to make it too complex, I've opted to have one action called authenticateUser that gets called on click and that handles the logic to login to the first provider and then the second.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Nov 15 '18 at 17:45

























                answered Nov 15 '18 at 17:34









                woolm110woolm110

                6831023




                6831023
































                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53323104%2fredux-store-not-up-to-date-in-component-did-update%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Xamarin.iOS Cant Deploy on Iphone

                    Glorious Revolution

                    Dulmage-Mendelsohn matrix decomposition in Python