Implementing reselect in redux prevents new changes to appear instantly - reactjs

In my React project, I have implemented memoization using reselect library.
The state basically has a list of objects which I render as cards.
Before implementing reselect, whenever I added a new element, the change instantly showed up and a new card got added at the end. However, now when I add a new element it does not instantly shows up, but rather shows up when the page is reloaded.
Why does this happen? And is there a way to fix this without removing the use of reselect library
EDIT : The issue has been solved, and as pointed out in the answers it was because I was simply mutating the state
The earlier code was as follows
case IssueActionTypes.ADD_ISSUE:
state.issueList.push(action.payload)
return {
...state
}
which I replaced with
case IssueActionTypes.ADD_ISSUE:
return {
...state,
issueList : [...state.issueList, action.payload]
}
which fixed the issue

Most likely you are returning mutated state in your reducers instead of returning a new array.
Docs:
createSelector uses an identity check (===) to detect that an input
has changed, so mutating an existing object will not trigger the
selector to recompute because mutating an object does not change its
identity. Note that if you are using Redux, mutating the state object
is almost certainly a mistake.
Example of returning mutated state (from docs):
export default function todos(state = initialState, action) {
switch (action.type) {
case COMPLETE_ALL:
const areAllMarked = state.every(todo => todo.completed)
// BAD: mutating an existing object
return state.map(todo => {
todo.completed = !areAllMarked
return todo
})
default:
return state
}
}

Related

React DOM not updated when prop from redux store changes

This is driving me crazy for hours now... I have a module that displays a list that is fetched from a server and loaded into the redux store on button press. That works properly. I mention this as this is the reason why I don't understand the following behavior.
This object array from the store is mapped into my component with
const mapStateToProps = (state) => {
return {
extracted_templates: state.extracted_templates
}
}
And used in the render() as follows... I removed some other DOM parts to keep it simple
render(){
return(
<div className="main-container">
{Object.values(this.props.extracted_templates).length > 0 ?
<ExtractedTemplatesList templates={Object.entries(this.props.extracted_templates)} clickHandler={this.clickHandler} /> : '' }
</div>
);
}
The clickHandler modifies the store using the same action as the fetch function uses.
clickHandler(action, id, parent){
console.log(action+" "+parent)
switch(action){
case 'dismiss':
let new_template_list = this.props.extracted_templates
delete new_template_list[id]
// console.log(new_template_list)
this.props.dispatch(setExtractedTemplates(new_template_list))
break;
default:
break;
}
}
Everything is called correctly, the store updates correctly (as I can see in my web-dev console) but this time the DOM doesn't get updated.
For completeness, here's the action and the reducer implementation
action:
export const setExtractedTemplates = (templates) => ({
type: actions.SET_EXTRACTED_TEMPLATES,
payload: templates
});
reducer:
case actions.SET_EXTRACTED_TEMPLATES:
console.log({action})
return {
...state,
extracted_templates: action.payload
}
You're mutating the existing data, and you're putting the exact same object back into the store:
let new_template_list = this.props.extracted_templates
delete new_template_list[id]
this.props.dispatch(setExtractedTemplates(new_template_list))
Both of those are bugs. You should never mutate data from the store, and the result of an action should be new data in the store.
This is one of the reasons why we recommend putting as much logic as possible into reducers. Also, you should be using our official Redux Toolkit package, which would both catch this accidental mutation here, and simplify the update logic in a reducer.
Try this:
clickHandler(action, id, parent){
console.log(action+" "+parent)
switch(action){
case 'dismiss':
let new_template_list = {...this.props.extracted_templates} //make a new copy
delete new_template_list[id]
// console.log(new_template_list)
this.props.dispatch(setExtractedTemplates(new_template_list))
break;
default:
break;
}
}
You modified the same object saved in the redux store. This is potentially dangerous because you changed the state without using a reducer. When React did the shallow comparison, it didn't see difference so UI was not updated. You can make a copy before save it to store.
Further more you can modify your reducer in this way:
case actions.SET_EXTRACTED_TEMPLATES:
console.log({action})
return {
...state,
extracted_templates: [...action.payload] //make a new copy
}

Immutability in react redux reducer

If I write my reducer in the following way, then the "render" method is getting called and its a expected behavior. No problem here :
const initState = {
entries: ["test1"]
};
export const EntryReducer = (state = initState, action) => {
switch (action.type) {
case ADD_ENTRY:
return Object.assign({}, state, {
entries: state.entries.concat("test2")
});
break;
case DELETE_ENTRY:
break;
}
return state;
}
But, if I write the reducer in the following way, then the "render" method is not getting called though state is being updated :
export const EntryReducer = (state = initState, action) => {
let newState = Object.assign({}, state);
switch (action.type) {
case ADD_ENTRY:
newState.entries.push("test2");
break;
case DELETE_ENTRY:
break;
}
return newState;
}
I could not understand why render is not getting called. As far as I understand, "newState" is a immutable object and does not hold any reference to the "state" object.
Please help me understand it. Thanks.
Because Object.assign is shallow, it will not create a new Array, the old one will be mutated by .push():
state.entries === newState.entries // ["test1", "test2"]
"newState" is a immutable object
If you don't do that on your own, it's ordinary object/array/etc so it is not immutable.
why render is not getting called
React-redux tries its best and actually wraps your component into PureComponent. It does that to make all your connect()-ed components will not re-render on any action called but only once store has been updated in relevant places.
To realize if relevant data has been changed or not, PureComponent compares shallowly like oldDataProp !== newDataProp. Once you mutate objects/arrays that check will fail so component will not re-render.
It has nothing to do with the way you update the state. Consider it correct, though I would not recommend to avoid common best practices.
In your case render is not called because component props is not changed...
I believe that you may have something like this:
<Component entries={this.props.entries}/>
and
mapStateToProps = (state) => ({
entries: state.entries
})
If it is so, then state.entries is the prop that controls whether your component will be re-rendered or not. If it has the same value during the state change on ADD_ENTRY action - the component will not be re-rendered.
So. Get back to roots. Remember that in JavaScript state.entries is a POINTER, that points to array in memory.
While you calling entries.push the array in memory will be extended with another element - thats for sure. BUT the POINTER value that we have in state.entries will remain the same. It will be not changed. Thats how Array.push works.
As the result <Component entries={this.props.entries}/> will not be re-rendered.
By changing newState.entries.push("test2");
to
newState.entries = newState.entries.concat("test2");
you actually changing entries pointer, component sees that property props.entries has changed and now the whole thing re-renders...
You need to use other alternatives because Object.assign() copies property values. If the source value is a reference to an object, it only copies that reference value.
You should use a Deep clone.
let newState= JSON.parse(JSON.stringify(state));

Updating string in redux store

I am using redux with react. I have a key in store whose value is a string.
This is what is get in store.getState();
{appBgColor: "#FFF"}
I am updating the store like this.
store.dispatch( changeAppBgColor("#FC0") );
Reducer
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case CHANGE_APP_BG:
return { ...state, appBgColor: action.payload};
default:
return state;
}
};
Everything works fine, the appBgColor get changed whenever a new dispatch happens.
Problem
I have read that to change a key in redux store you should use concat, slice or join to make the state immutable. So I doubt I am doing it wrong. Is this the correct way to do it?
For your use-case of updating the field appBgColor within your state, your reducer implementation is correct.
Note that when you return { ...state, appBgColor: action.payload}, you are not mutating the state, but in fact, creating a copy of the existing state, applying the change and returning it. This makes sure that the state is immutable, ie it is not directly modified.
You would only need to use functions like slice, concat etc when you are updating nested items within your state. For eg, when you need to remove an element from an array within your state, you would need to use slice as shown below.
const index = state.findIndex(a => a.id === action.id)
return [
...state.slice(0, index), ...state.slice(index + 1)
]

What is the best practice to return a new state from reducers in react/redux?

What is the best practice to return a new state from reducers - is it by using $set and update from immutability-helpers, or by using Object.assign()?
Personally I wouldn't use immutability helpers when working on a project where it's easy to manage the state. If you have a really large state it can be helpful to use immutability helpers so you won't be that likely to make mistakes.
And for returning the state in a reducer, it's a common practice to return the new state by using the spread operator. This would look something like this:
return {...state, newProp: 'value', etc...}
you can create new state in reducer in redux and also change the state by using set method and you can return into your index page as props
ex:in reducer you write as follows
return state.set('tasks', action.response)
At first, you should need a clear understanding of redux. it's a back store on your frontend. redux store data as a stacking technique FIFO or LIFO as you defined. You don't make it reassign, just return with action-object using ES6 SPREAD operator and using destructuring. I try to give an example below:
const postsReducerDefaultState = [];
const postsReducer = (state=postsReducerDefaultState, action) => {
switch(action.type) {
case 'ADD_POST':
return [action.post, ...state];
case 'REMOVE_POST':
return state.filter(post => post.id !== action.id)
case 'SET_POSTS':
return action.posts;
default:
return state;
}
};
export default postsReducer;
ADD_POST: return [action.post, ...state] latest first then spreading your old post.SET_POSTS: return action.posts that you have passed with your action. If you want to set an object property then use destructuring
return {
...state,
text:action.text
}
text property already have in your state. if haven't then it will add a property to your state. I think it helps you to understand something redux.

Update state in deep structure not re-rendering component

I just got started with React+Redux and I have a problem.
I know that I am never supposed to alter the old state in the reducer and I am not doing that.
However, when I change a variable like this in my reducer, my component is not re-rendering, even though I have mapStateToProps with state.coupons
// this deep copies everything
let newState = Object.assign({}, state);
newState.coupons[2].events[0].eventRows[0].alternatives[0].selected = true;
return newState;
What am I doing wrong?
EDIT:
I even tested to use newState = JSON.stringify(JSON.parse(oldState)) but with no success
EDIT:
This is my mapStateToProps function
const mapStateToProps = (state, ownProps) => ({
coupons: state.coupons,
currentDraw: state.currentDraw
});
You can find the solution in redux docs: http://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html#updating-nested-objects
The key to updating nested data is that every level of nesting must be
copied and updated appropriately. This is often a difficult concept
for those learning Redux, and there are some specific problems that
frequently occur when trying to update nested objects. These lead to
accidental direct mutation, and should be avoided.
You can do this manually, something like:
function updateVeryNestedField(state, action) {
return {
....state,
first : {
...state.first,
second : {
...state.first.second,
[action.someId] : {
...state.first.second[action.someId],
fourth : action.someValue
}
}
}
}
}
In practice, it's better to use a helper library to do this. You can find a list of helper libraries at https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities, I would personally recommend immutability-helper or just switching to immutable.js.

Resources