Redux store not up to date in component did update
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
add a comment |
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
add a comment |
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
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
javascript reactjs redux redux-thunk
edited Nov 15 '18 at 16:39
woolm110
asked Nov 15 '18 at 15:48
woolm110woolm110
6831023
6831023
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
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.
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
add a comment |
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.
add a comment |
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.
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.
edited Nov 15 '18 at 17:45
answered Nov 15 '18 at 17:34
woolm110woolm110
6831023
6831023
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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