redux not calling mapStateToProps on store update - reactjs

Do not use push while updating your state. Use concat
I'm facing this really strange issue, consider this reducer:
export default function(state = null, action) {
console.log("Current State: ",state);
// on performing actions, it gives me:
// Current State: null
// Current State: Array [{}]
// Current State: Array [{}] -- all good
if(state === null) {
state = [
{id: 1, title: "Java"}
];
}
// UPDATED PART. FORGOT TO MENTION IT BEFORE
if(Action.type == "UPDATE_LIST") {
state.push( Action.payload ); // don't do that, this'll mutate your array and states are immutable
}
/////////////
return state; // this is the main problem
}
The above code does not invoke mapStateToProps inside my component. However, modifying the above reducer like down below does invoke mapStateToProps:
return []; // instead of return state;
OR
return [ {id: 1, title: "Python"} ]; // instead of return state;
I'm returning instanceof Array in both cases [ state & [] ], but only the latter one is invoking mapStateToProps in my component.
This is strange, and I've no idea what am I suppose to do to fix this.

The point of redux is to ensure that your state is not directly mutable. Since Arrays and objects are passed by reference in Javascript, your code is attempting to mutate the state object directly..which is incorrect.
Always mutate the state by returning the new state. Like this:
export default function(state = null, action) {
let newState = [...state];
if(state === null) {
newstate = [
{id: 1, title: "Java"}
];
}
return newState;
}

do like this way :
if(state === null) {
state = [
{id: 1, title: "Java"}
];
return state;
}
return state;

Related

How to get old state of an array that is inside and object with spread operator in Redux

i'm tryin to get the old state of my productData[] with spread operator but this array is inside in object.
I have to keep old state because i need to keep my products that are actually in state and to add new products here in Redux.
i try this:
case 'ADD_TO_COMPARE':
return {
productData:[...state.productData,action.payload],
open:true,
}
but didnt work and here is only last product i put here.
Here is my code in reducer on Redux:
const initialValue = {
productData:[],
open:false
}
export const compareReducer = (state = initialValue, action) => {
switch (action.type) {
case 'ADD_TO_COMPARE':
return {
productData:[...state.productData,action.payload],
open:true,
}
case 'REMOVE_FROM_COMPARE':
return initialValue
default:
return state
}
}
My Console:
2 first products are added hard code and the when the third product is added, always override the last product that is added here in state
I don't see an obvious issue with the way you're merging the productData. I suspect either state.productData is empty on the way in or action.type isn't a match.
Syntactically what you're doing works:
// old state
const state = {
productData: [{id: 1}, {id: 2}],
otherStuff: 'bananas and wookies'
}
// action
const action = {
payload: [{id: 3}, {id: 4}]
}
const merged = { // new object literal
...state, // keep old state properties
productData: [ // overwrite the 'productData' from state with
...state.productData, // the previous state's product data
...action.payload // and the action's payload
]};
console.log(merged);
/*
{
"productData": [
{ "id": 1 }, <-- original state
{ "id": 2 },
{ "id": 3 }, <-- action payload
{ "id": 4 }
],
"otherStuff": "bananas and wookies" <-- original state
}
*/

Updating an item in Redux using ImmutableJS given a key

Redux State:
let initialState = Immutable.fromJS({
budgetItems: [],
editingBudget: [[]]
});
Trying to update items in the budgetItems section, which is an array of objects.
The structure of the objects in the array is like:
let initBudget = {budgetCategory: '', budgetCost: '', budgetDate: ''};
My attempt:
case types.UPDATE_EXISTING_BUDGET:
return state.getIn(['budgetItems']).update(
state.getIn(['budgetItems']).findIndex(function(item) {
return item.get("budgetCategory") === action.payload.budgetCategory;
}), function(item) {
return item.set(action.payload.budgetCategory);
}
);
Trying to literally replace the entire object that I've found. The above code is working if I set a single key with a value, but not the entire object
To update an object or an array you'll need the updateIn method.
import { fromJS, List, Map } from immutable;
const initialState = fromJS({
data: List([Map({ foo: { bar: baz } } })],
});
functionFooReducer(state = initialState, action) {
switch (action.type) {
case UPDATE:
return state
.updateIn(['data.0.foo.bar'.split('.')], (), action.value);
// ...
}
}

Updating state with nested array of objects

This is something also called as deep state update. Where I have to update nested state array.
I am implementing a simple redux application. Here I want to update the state which is nested array of object. My reducer function takes state, action. I have to update responses property of state with new value. I tried to map/iterate the state but it isnt working for me. Is there a way to update those specific values and return update state.
const sampleData = [{
"id": 1,
"text": "Hobby",
"answers": [{
"id": 1,
"text": "Chess",
"responses": 5
}]
}];
const action = {
type: "VOTE",
questionId: 1,
answerId: 3
};
This is handleSubmit function I am calling when Submit button is clicked on form.
handleSubmit(e){
const store = createStore(hobbyReducer, hobby); // created store here
var k = (document.querySelector('input[name = "hobby"]:checked').value);
store.dispatch({type:"SUBMIT", text: k, id: 1}); // Dispatching action to reducer
store.subscribe(()=>{
console.log(store.getState());
});
}
Here is reducer part of program:
function hobbyReducer(state, action) {
switch(action.type){
case "SUBMIT":
return{
...state,
answers: state.answers.map(e=> (e.text === action.text && e.answers.id === action.id)?
{ ...answers,
responses: (e.responses+1)} :
hobby )
}
break;
default:
return state;
}
}
initial state = sampleData; // Array object given above
I am unable to update the responses property which is in a nested array
This is the code I wanted to write, after some research I finally did what was required. Although this is not solution in terms of time complexity.
`
case "SUBMIT":
const updatedState = state.map(e =>{
if(e.id === action.id)
return{...e,
answers: e.answers.map(f =>{
if(f.text === action.text){
return{
...f,
...{responses: f.responses + 1},
}
}
return f;
})
}
return e;
})
console.log("updatedstate", updatedState)
return updatedState
Just an error in your map I think:
function hobbyReducer(state, action) {
switch(action.type) {
case "SUBMIT":
return {
...state,
answers: state.answers.map(answer => {
if (answer.text === action.text && answer.id === action.id) {
answer.response = answer.response + 1;
}
return answer;
});
}
default:
return state;
}
}

How to replace part of the Redux state

I have a packageReducer which keeps the packageType and packageList related details. once the details are fetched from the server I need to replace the initial state values with the new values that are been fetched. As an example, if the packageLists are been fetched I need to replace only the "packageList"
Below is my PackageState reducer,
const initialState = {
packageList: packageListInitialState,
packageTypes: [{title: 'Select Package Type', value: ''}],
};
export default function packageState( state = initialState, action ) {
switch ( action.type ) {
case FETCH_PACKAGE_LIST_SUCCESS:{
return Object.assign( {}, state, action.payload );
}
case FETCH_PACKAGE_TYPES_SUCCESS:{
return Object.assign( {}, state, action.payload );
}
default:
return state;
}
}
The way I have implemented I think im replacing the entire state, Can someone let me know how I can achieve it?
Thank you.
You are not:
var state = {a: 1, b: 2, c: 3}
var newData = {a: 4, b: 5}
console.log(Object.assign( {}, state, newData )) // { a: 4, b: 5, c: 3 }
Properties in the target object will be overwritten by properties in the sources if they have the same key. Later sources' properties will similarly overwrite earlier ones. (docs)
So, as long as your payload contains the keys that you really want to update, you are safe. You can also do it in a simpler way if you use ES6's spread syntax (I'm assuming your payload looks like {packageList: data}):
const initialState = {
packageList: packageListInitialState,
packageTypes: [{title: 'Select Package Type', value: ''}],
};
export default function packageState( state = initialState, action ) {
switch ( action.type ) {
case FETCH_PACKAGE_LIST_SUCCESS:{
return {...state, ...action.payload};
}
case FETCH_PACKAGE_SETTINGS_SUCCESS:{
return {...state, ...action.payload};
}
default:
return state;
}
}
Under the assumption that the action you're trying to achieve this in is only the FETCH_PACKAGE_LIST_SUCCESS action, and the payload is the updated/fetched list, then you just need to return an object as shown below.
Since you're trying to return an object with only one of the two properties changed, then you can use the previous state's value for the unchanged property and update the other.
const initialState = {
packageList: packageListInitialState,
packageTypes: [{title: 'Select Package Type', value: ''}],
};
export default function packageState( state = initialState, action ) {
switch ( action.type ) {
case FETCH_PACKAGE_LIST_SUCCESS:{
return { packageList: action.payload, packageTypes: state.packageTypes }
}
case FETCH_PACKAGE_SETTINGS_SUCCESS:{
return Object.assign( {}, state, action.payload );
}
default:
return state;
}
}

React redux - issues adding multiple items to an array in state tree object

I am looking at redux and adding names to an array. The code below works (kind of!).
I have a few issues.
I know that it is advised to create a new state tree object each time the state is passed through the reducer, however I thought it should still work even if I change the state object passed in.
In my code below the console.log(store.getState()); works if I use var newArr = state.names.concat(action.name); but not if I use state.names.push(action.name);
If I add another store.dispatch(action) the code doesn't work.
store.dispatch({type: 'ADD_NAME',name: 'PhantomTwo'});
Can anyone explain why this is so?
Finally, do I need to return state again outside the switch statement?
Here is the code I currently have below.
const initialState = {
names: []
}
function namesApp(state = initialState, action) {
switch(action.type) {
case 'ADD_NAME':
var newArr = state.names.concat(action.name);
return newArr;
default:
return state;
}
}
let store = createStore(namesApp);
store.dispatch({
type: 'ADD_NAME',
name: 'Phantom'
});
console.log(store.getState()); //returns `["Phantom"]`
This is the behavior of array object mutability
Since React highly cares about state change for re-rendering, so we need to take care of mutability.
The below snippet explains the array mutability.
let x = [];
let y = x;
console.log(x);
console.log(y);
y.push("First");
console.log(x);
console.log(y);
let z = [...x]; //creating new reference
console.log(z);
x.push("Second");
console.log(x); //updated
console.log(y); //updated
console.log(z); //not updated
So for better functionality your reducer will be like
function namesApp(state = initialState, action) {
switch(action.type) {
case 'ADD_NAME':
return {
...state, //optional, necessary if state have other data than names
...{
names: [...state.names, action.name]
}
};
default:
return state;
}
}
[].concat returns a new array. But your state was { name: [] }. Inspite of returning newly build object with new names, the code above returned the new names array.
Vanilla solution
const initialState = { names: [] };
function namesApp(state = initialState, action) {
switch(action.type) {
case 'ADD_NAME':
var newArr = state.names.concat(action.name);
return {
...state,
names: newArr
};
default:
return state;
}
}
immutability-helper
For this type of work I would use immutability-helper
import u from 'immutability-helper';
function namesApp(state = initialState, action) {
switch(action.type) {
case 'ADD_NAME':
return u(state, {
names: {
$push: action.name
}
});
default:
return state;
}
}
learn how to use immutability-helper https://facebook.github.io/react/docs/update.html

Resources