React redux two states conflict - reactjs

I have two reducers in my redux mention below -
case actionTypes.SENT_REQUEST_FAIL:
return {
showAlert: true,
}
case actionTypes.GET_STUDENT_PROFILE_BY_PROFILEID_SUCCESS:
return {
showAlert: false,
}
and i need showAlert:true in some case but last reducer call is GET_STUDENT_PROFILE_BY_PROFILEID_SUCCESS , so always showAlert become false. how do i run SENT_REQUEST_FAIL in end to get true value.
const cardData = async (val1, val2, val3) => {
setshowsentRequestModal(false);
handleModal(false);
//this is true one
await props.sentRequest(val1, val2, val3);
};
const handleModal = (val) => {
props.handleSentRequestModal(val);
//this is false one
props.getStudentProfileByProfileId(studentId);
};

In your approach, state is not update. It always return the initial value.
you need to pass the current state to reducer and update it as below.
case actionTypes.SENT_REQUEST_FAIL:
return {
...state, showAlert: true
};
case actionTypes.GET_STUDENT_PROFILE_BY_PROFILEID_SUCCESS:
return {
...state, showAlert: false
};

Related

Redux reducer: Add object to array when the array doesn't have the object data

I'm trying to store AJAX call returned data object to an array of reducer state of Redux.
I have some conditions to check if the fetched data already exists inside of the reducer.
Here are my problems:
The component to call AJAX call actions, it's a function from mapDispatchToProps, causes an infinite loop.
isProductLikedData state in reducer doesn't get updated properly.
Can you tell what I'm missing?
Here are my code:
isProductLikedActions.js - action to fetch isProductLiked data. response.data looks like {status: 200, isProductLiked: boolean}
export function fetchIsProductLiked(productId) {
return function (dispatch) {
axios
.get(`/ajax/app/is_product_liked/${productId}`)
.then((response) => {
dispatch({
type: 'FETCH_IS_PRODUCT_LIKED_SUCCESS',
payload: { ...response.data, productId },
});
})
.catch((err) => {
dispatch({
type: 'FETCH_IS_PRODUCT_LIKED_REJECTED',
payload: err,
});
});
};
}
isProductLikedReducer.js - I add action.payload object to isProductLikedData array when array.length === 0. After that, I want to check if action.payload object exists in isProductLikedData or not to prevent the duplication. If there is not duplication, I want to do like [...state.isProductLikedData, action.payload].
const initialState = {
isProductLikedData: [],
fetching: false,
fetched: false,
error: null,
};
export default function reducer(state = initialState, action) {
switch (action.type) {
case 'FETCH_IS_PRODUCT_LIKED': {
return { ...state, fetching: true };
}
case 'FETCH_IS_PRODUCT_LIKED_SUCCESS': {
return {
...state,
fetching: false,
fetched: true,
isProductLikedData:
state.isProductLikedData.length === 0
? [action.payload]
: state.isProductLikedData.map((data) => {
if (data.productId === action.payload.productId) return;
if (data.productId !== action.payload.productId)
return action.payload ;
}),
};
}
case 'FETCH_IS_PRODUCT_LIKED_REJECTED': {
return {
...state,
fetching: false,
error: action.payload,
};
}
}
return state;
}
Products.js - products is an array that fetched in componentWillMount. Once nextProps.products.fetched becomes true, I want to call fetchIsProductLiked to get isProductLiked` data. But this causes an infinite loop.
class Products extends React.Component {
...
componentWillMount() {
this.props.fetchProducts();
}
...
componentWillReceiveProps(nextProps) {
if (nextProps.products.fetched) {
nextProps.products.map((product) => {
this.props.fetchIsProductLiked(product.id);
}
}
render() {
...
}
}
export default Products;
Issue 1
The component to call AJAX call actions, it's a function from mapDispatchToProps, causes an infinite loop.
You are seeing infinite calls because of the condition you used in componentWillReceiveProps.
nextProps.products.fetched is always true after products (data) have been fetched. Also, note that componentWillReceiveProps will be called every time there is change in props. This caused infinite calls.
Solution 1
If you want to call fetchIsProductLiked after products data has been fetched, it is better to compare the old products data with the new one in componentWillReceiveProps as below:
componentWillReceiveProps(nextProps) {
if (nextProps.products !== this.props.products) {
nextProps.products.forEach((product) => {
this.props.fetchIsProductLiked(product.id);
});
}
}
Note: you should start using componentDidUpdate as componentWillReceiveProps is getting deprecated.
Issue 2
isProductLikedData state in reducer doesn't get updated properly.
It is not getting updated because you have used map. Map returns a new array (having elements returned from the callback) of the same length (but you expected to add a new element).
Solution 2
If you want to update the data only when it is not already present in the State, you can use some to check if the data exists. And, push the new data using spread syntax when it returned false:
case "FETCH_IS_PRODUCT_LIKED_SUCCESS": {
return {
...state,
fetching: false,
fetched: true,
isProductLikedData: state.isProductLikedData.some(
(d) => d.productId === action.payload.productId
)
? state.isProductLikedData
: [...state.isProductLikedData, action.payload],
};
}

Update the Old and New Value in Redux

I have a problem updating the old value of the array in my redux react app. I have successfully updated the new selected object true. I want to other object to set to false since I have set the another object to true.
const initialState = {
annualPlans: [],
};
const planReducer = (state = initialState, action) => {
switch (action.type) {
case planConstants.UPGRADE_PLAN_SUCCESS:
return {
...state,
annualPlans: state.annualPlans.map((todo) =>
todo.value === action.data.plan_id
? // transform the one with a matching id
{ ...todo, current: true }
: // otherwise return original todo
todo
),
};
default:
return state;
}
};
It seems like you want to return current set to false for the others:
todo.value === action.data.plan_id
? // transform the one with a matching id
{ ...todo, current: true }
: // otherwise return with current false
{ ...todo, current: false }
Id first create the new todos by looping though a map and then assign to the state
case planConstants.UPGRADE_PLAN_SUCCESS: {
const newTodos = state.annualPlans.map((todo) => {
if (todo.value === action.data.plan_id) {
return { ...todo, current: true }; // if todo.id matched then set the current to true and return;
}
if (todo.current) {
return { ...todo, current: false }; // else if current is already true, then false it and return
}
return todo; // else return original todo
});
return {
...state,
annualPlans: newTodos
};
}
.....
This will optimize the rendering and prevent of looping multiple times.
No need to re create all todo items, if you use pure components then that will mess up this optimisation. You can just add a map to deal with resetting the current value:
const planReducer = (state = initialState, action) => {
switch (action.type) {
case planConstants.UPGRADE_PLAN_SUCCESS:
return {
...state,
annualPlans: state.annualPlans
.map(
(todo) =>
todo.current
? { ...todo, current: false } //reset current to false
: todo // no need to change this one
)
.map((todo) =>
todo.value === action.data.plan_id
? // transform the one with a matching id
{ ...todo, current: true }
: // otherwise return original todo
todo
),
};
default:
return state;
}
};

Redux: Action is being called properly but reducer is not updating the state, just keeping the initial state

I have been building an application using React, Redux, Redux-thunk.
While calling action, the payload and type are properly received by the reducer but it is not modifying them.
My Reducer:
import EditorActionTypes from './editor.types';
const INITIAL_STATE = {
editorModified: false,
tabs: [
{
name: 'main.js',
active: true
},
{
name: 'setup.js',
active: false
}
]
};
const editorReducer = ( state = INITIAL_STATE, action ) => {
switch (action.payload) {
case EditorActionTypes.SELECT_TAB:
return {
...state,
tabs: [
...state.tabs.map(
(tab, index) => index === action.payload
? tab.active = true
: tab.active = false
)
]
}
default:
return state;
}
};
export default editorReducer;
Switch condition is wrong,
Reducer function should check for action.type
switch (action.type) {
case EditorActionTypes.SELECT_TAB:
return {
The problematic part, I think, is this:
...state.tabs.map(
(tab, index) => index === action.payload
? tab.active = true
: tab.active = false
)
You are not returning anything from the function inside map. You should return tab objects, instead.
...state.tabs.map(
(tab, index) => ({
...tab,
isActive: index === action.payload
}))
And as #Gangadhar Gandi pointed out, you should switch on the action.type, not the action.payload

React Native ShouldComponentUpdate not firing between Redux Props Changes

In optimizing my component for performance, I noticed that sequential shouldComponentUpdate calls were seemingly missing the prop change from my redux store. An example being:
In props:
uiBehavior: {
shouldShow: false,
}
Redux Action:
fireActionToShow()
Redux Reducer:
case ActionType.UPDATE_UI_BEHAVIOR: return {...state, shouldShow: true}
Then I'm seeing:
shouldComponentUpdate(nextProps) {
// Would expect to at some point see:
this.props.shouldShow === false
nextProps === true
// Instead, only seeing
this.props.shouldShow === false
nextProps === false
// then
this.props.shouldShow === true
nextProps === true
}
** Note that this clearly isn't the code, just an example
It seems to me that a prop change isn't causing a rerender attempt, or am I missing something?
Thanks.
Expanding for clarity, here is some of the real code:
*** the event in the Action updates the uiBehavior prop on the redux store.
componentDidUpdate(prevProps, prevState) {
const { uiBehavior } = this.props;
if (!_.isEqual(uiBehavior.lockAttributes, prevProps.uiBehavior.lockAttributes)) {
console.log('Lock has changed.'); // This never gets called
}
}
const mapStateToProps = function(state){
return {
uiBehavior: state.uiBehavior,
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SlotMachine)
*** UPDATE
JS Call:
this.props.setGeneralUiKeyValue('lockAttributesInCart', !lockAttributes);
Action:
export const setGeneralUiKeyValue = (key, value) => { return { type: GENERAL_UI_UPDATE, key, value }}
Reducer:
export const uiBehavior = (state = {}, action) => {
let newUiState = {...defaultState, ...state}
switch (action.type) {
case uiActionTypes.GENERAL_UI_UPDATE:
newUiState.general = newUiState.general || {};
newUiState.general[action.key] = action.value;
return newUiState
default:
return newUiState;
}
return newUiState

Redux state coming out to be undefined

I am having tough time figuring out why my redux state is coming out to be undefined on a later stage.
so I have an action which looks like this
export const coinUpdateState = (booleanValue) => {
console.log(typeof booleanValue) //This logs Boolean
return function (dispatch) {
dispatch({
type: COIN_UPDATE_STATE,
payload: booleanValue
})
}
}
and a reducer which looks like this
const initialState = {
DataFetching: true,
DataSucess: [],
DateError: [],
DateError: false,
DataSort: true,
DataUpdate: true
}
export default function(state = initialState, action) {
switch (action.type) {
case EXCHANGE_CURRENCY_FETCHING:
return {
DataFetching: true,
DataSort: true
}
case EXCHANGE_CURRENCY_FETCH_SUCCESS:
return {
...state,
DataSucess: action.payload,
DataFetching: false,
DataSort: false
}
case EXCHANGE_CURRENCY_FETCH_ERROR:
return {
...state,
DateError: action.payload,
DataFetching: true,
DateError: true,
DataSort: true
}
case COIN_UPDATE_STATE:
console.log(action.payload) //This logs the boolean value I am sending
return {
DataUpdate: action.payload
}
default:
return state
}
}
Later I am using it like this in my app
render () {
console.log(this.props.cryptoUpdateState)
if (this.props.cryptoUpdateState) {
console.log("Inside if render")
displayData = async () => {
this.coinURL = await AsyncStorage.getItem("CryptoCurrencySelected").catch((error) => {
console.log(error)
})
if (this.coinURL != "undefined" && this.coinURL != null) {
console.log("Inside Async", this.coinURL)
this.props.exchangeToDisplay(this.coinURL)
}
if (this.coinURL == null || this.coinURL == "undefined") {
console.log("Null", this.coinURL)
this.coinURL = "BTC"
}
}
displayData()
this.props.coinUpdateState(false)
}
In the above snippet notice the initial console.log, it logs true correctly the first time, there after it logs undefined in console when I am actually passing false (this.props.coinUpdateState(false)).
Also Notice the logs in my code, they are logging the value I am sending correctly everywhere besides in the later stage where it is logging undefined (in console.log).
Question: What could I be doing wrong here?
A reducer will return a new version of the state of your app. If you don't want to lose your previous state you will need to make sure you always return it with any additions/modifications you need.
Whatever is returned from the switch case that is triggered is going to be the next version of state, which for COIN_UPDATE_STATE is going to just have DataUpdate in.
Make sure all you are doing ...state for all your redux reducer actions in order to make sure you keep state.
e.g.
case COIN_UPDATE_STATE:
return {
...state,
DataUpdate: action.payload
}

Resources