Redux state is not updating into the react component - reactjs

How the redux state is updated into the react component?
I tried using initial state and not mutating the object and return that
Redux
const initialState = {
filteredProviderData:[],
filteredAlsoSpeaksData:[],
filteredOfficeHours:[]
};
function reducer(state = initialState, action = {}) {
switch (action.type) {
case HANDLE_FILTER_CHANGE:
let filteredProviderData = '';
let filteredAlsoSpeaksData='';
let filteredOfficeHours = ''
return {...state, filteredProviderData,filteredAlsoSpeaksData,filteredOfficeHours};
case RESET_FILTER_COLLECTION:
// RESET the Array as shown into the HANDLE_FILTER_CHANGE
}}
React component
const mapStateToProps = state => {
return {
filteredProviderData:state.providerList && state.providerList.filteredProviderData,
filteredAlsoSpeaksData:state.providerList && state.providerList.filteredAlsoSpeaksData,
filteredOfficeHours:state.providerList && state.providerList.filteredOfficeHours
}}
Here my question is how to update the array which is into the HANDLE_FILTER_CHANGE
RESET_FILTER_COLLECTION
I need to update array based on some condition and return that updated array
Here into the based on the condition only one array will be updated and return that only two would remains same.

You need to change the local state once you updated the redux store. this.setState({loading:true}) Kind of approach will work and it works for me too.

Related

Redux reducer doesn't cause re-render after mapping to new array or using immer

In my redux reducer I map over state and copy internal items, then return the list, the list should be a new reference so the reducer should cause a re-render on change, but it doesn't.
The code below does not cause a re-render.
const initialState: Group[] = [];
export default function activeGroups(state = initialState, action: AnyAction) {
switch (action.type) {
case 'groups/createPod/fulfilled': {
// append the new pod into the active group that matches the groupId
const { pod, groupId } = action.payload;
const newState = state.map((group) => { // Map to new list
if (group.id === groupId) {
return {
...group, // Not mutating original state, copying the state into new list
pods: [pod, ...group.pods],
};
}
return group;
});
return newState; // This does not cause a re-render, why?
}
I've tried produce from immer
case 'groups/createPod/fulfilled': {
// append the new pod into the active group that matches the groupId
const nextState = produce(state, (draft) => {
const group = draft.find((e) => e.id === action.payload.groupId);
if (group) {
group.pods.unshift(action.payload.pod);
}
});
return JSON.parse(JSON.stringify(nextState)); // Even tried this
}
Figured it out. Redux is re-rendering as normal. The problem was unrelated to Redux.
It was because the object I was trying to update was being passed as a navigation param in react native, and this does not update with state changes (the reference is to the object passed through navigation, not to the Redux state).
Using useSelector() solves it.

React : reducer isn't updating my state when using Object.assign

I use a reducer to update this state :
const [playlist, dispatch] = useReducer(jspfReducer,new playlistModel());
It uses my playlistModel class, which is just a wrapper that adds some methods to manipulate easily my data - I need it.
I want to update the state, but avoid as much possible unecessary renders.
So when calling the reducer case UPDATE_JSPF_TRACK; I update only the matching track of the playlistModel track array.
function jspfReducer(state, action){
switch (action.type) {
case 'UPDATE_JSPF_TRACK':
const [index,jspf] = action.payload;
//update only that single track.
const newTracks = state.track.map(
(track, i) => i === index ? new trackModel(jspf) : track
);
const newState = Object.assign(
state,
{
track:newTracks
}
);
//my object value is correctly updated here :
console.log("NEW STATE",newState);
return newState;
break;
}
};
The value logged in the console is correctly updated.
But in my provider, the state update is not detected:
export function PlaylistProvider({children}){
const [playlist, dispatch] = useReducer(jspfReducer,new playlistModel());
//when state updates
useEffect(()=>{
console.log("PLAYLIST HAS BEEN UPDATED!!!",playlist);//does not fire
},[playlist])
What is wrong and how could I fix this ?
Thanks !
You'll need to create a new object for the state changes to be detected:
const newState = Object.assign(
{},
state,
{
track: newTracks,
},
);
or
const newState = { ...state, track: newTracks };
The way React works is it detects if the state objects have different referential equality, which basically means that if you only modify an object's attributes, it will still be considered the same object for the purposes of rendering. To trigger state change effect you'll need to create a new object.

Component not updating on deeply nested redux object

I have a project portion of my app, and users can create events within the project. My redux state is deeply nested, which looks like:
When users create an event, I update state with the following:
case CREATE_PROJECT_TODO:
const data = [...state.data];
const index = state.data.findIndex(project => project._id===action.payload.project_id);
data[index].events = [
...data[index].events,
action.payload
];
return {
...state,
data
};
However, my react component isn't updating to reflect the changes. I'm quite sure I'm not mutating state. Is it an issue with deeply nested objects, and react can't detect those changes! Any help would be appreciated!
With const data = [...state.data], you are doing a shallow copy.
Use map and update your state. Your state is updated correctly and will trigger the component re-render properly.
case CREATE_PROJECT_TODO:
const index = state.data.findIndex((project) => project._id === action.payload.project_id)
const updatedData = state.data.map((item, idx) => {
if (idx === index) {
return {
...item,
events: [...item.events, action.payload],
}
}
return item
})

Changing state in Redux based on previous state

I am struggling a bit with Redux.
While I have managed to move some very simple states to Redux based on tutorials, I find it difficult to deal with more complex ones. Tried researching but answers vary and confuse.
The way I understand it, the equivalent of setState lies in Redux's actions and should be achieved by using getState() and then bt dispatch()ing the action to reducer...
If yes, then how to correctly translate the below example into Redux?
Let's say toggling a boolean of some nested music state element:
this.setState(prevState => {
const updatedMusic = prevState.music;
const elToUpdate = updatedMusic.musicStateItemList[3].favClicked;
elToUpdate = !elToUpdate;
return {
music: updatedMusic
};
});
the equivalent with your sample code in Redux is as below
const updatedMusic = state.music;
const elToUpdate = updatedMusic.musicStateItemList[3].favClicked;
updatedMusic.musicStateItemList[3].favClicked = !elToUpdate;
return {
...state,
music: [...updatedMusic]
};
it is about mutating state. You can use immutability helper if you want more advanced.
In the reducer the state is passed
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
let store = createStore(counter)
From the docs: https://redux.js.org/introduction/getting-started

How can I toggle property in reducer?

I have built a cart app with this reducer in reactjs/redux:
const initialState = {
items: [],
cartOpen: false,
total: 0
}
const Cart = (state = initialState, action) => {
switch (action.type) {
case 'ADD_TO_CART':
let newstate = [...state, action.payload];
var newTotal = 0;
newstate.forEach(it => {
newTotal += it.item.price;
});
newstate.total = newTotal;
newstate.cartOpen =true
return newstate;
case 'TOGGLE_CART':
debugger;
return !state.cartOpen;
default:
return state
}
}
export default Cart;
I am trying to set the state for the cart ie open but when I check the logs the cart property is updated and not the cartOpen property?
Redux assumes that you never mutate the objects it gives to you in the
reducer. Every single time, you must return the new state object.
Even if you don't use a library like Immutable, you need to completely
avoid mutation.
case 'TOGGLE_CART':
return !state.cartOpen;
Doing ^^ this is mutating your state (corrupting your state object). When you don't guarantee immutability, Redux loses its predictability and efficiency.
To achieve immutable state, we can use vanilla Object.assign or its more elegant alternative object spread syntax.
case 'TOGGLE_CART':
return {
...state,
cartOpen: !state.cartOpen
}
Your reducer must always return the complete slice of the app's state for which it is responsible. For TOGGLE_CART, you are only returning the boolean value for openCart.
Instead, create a copy of the previous state object and only update the single property you want to change:
case 'TOGGLE_CART':
return Object.assign({}, state, {
cartOpen: !state.cartOpen
});

Resources