Redux reducer adding _id to state - reactjs

I have a React / Redux application and am using redux-logger to show the state changes. I have some strange behaviour, wherby the resulting state contains an _id property. For example, this information below shows my output from the logger:
prev state:
{
title: ""
}
action object:
{
title: "New title"
}
next state:
{
_id: ObjectID,
title: "New title"
}
Below is my reducer function:
function updateState(state = initialState.obj, action) {
switch (action.type) {
case 'SET_TITLE':
var newState = Object.assign({}, state, action.obj);
return newState;
default:
return state;
}
}
and initialState is:
{
obj: {
title: ""
}
}
and the action payload is:
{
type: 'SET_TITLE',
obj: {
title: "New Title"
}
}
and the dispatcher is as follows:
// Call to API to get data from MongoDB...
let dataObj = response.data;
let newObj = {
title: dataObj.title
};
dispatch({
type: 'SET_TITLE',
obj: newObj
});
Why is the _id propery being added?

Nothing to worry about. Your initial state may even be an empty object.
{ }
And as your application traverses the states calling the reducer every time and creating new states
new_state = reducer_function(current_state, action);
Your Store object (state) may get different keys. You must try to understand what may enter the state, and to get the conclusion about the _id key.
Generally, the Store may be very complicated and big.
After you last update I understood the problem better, you are adding te object to the state.
var action = new Object;
action.title = "New Title";
var state = {
title: "Title",
desc: ""
}
console.log(state);
var newState = Object.assign({}, state, { title: action.title });
console.log(newState);
In your case "New Title" will be action.title.
This Object.assign() is new to ES6 btw.

Related

update array inside object inside another array

this is my reducer
let initialState = [
{ name: 'john', messages: [{ message: "hi" }] },
{ name: 'max', messages: [{ message: "howdy" }] },
{ name: 'alex', messages: [{ message: "hello" }] },
...
];
const Messages = (state = [], action) => {
switch (action.type) {
case "MESSAGES":
return [...state, ...action.payload];
case "UPDATE_ALEX_MESSAGES":
// HOW TO UPDATE????
default:
return state;
}
};
export default Messages;
how to update alex's messages and push another object to it? (commented part of the above code)
Note:
alex's position is not stable and it may change. so something like state[2] is not what I'm looking for. Something more like state.find(x => x.name === 'alex') is more like it..
The map() method and the spread syntax can help you achieve the desired result.
Using the map() method, iterate over the state array and inside the callback function, check if the current user object's name is "alex", if it is, update the current object's messages property by assigning a new array to it and using the spread syntax to copy the existing messages and then also add the new message in the array.
case "UPDATE_ALEX_MESSAGES":
return state.map((user) => {
if (name === "alex") {
user.messages = [...user.messages, newMessage];
}
return user;
});
You could also avoid mutating the existing object by returning a new object inside the if block.
case "UPDATE_ALEX_MESSAGES":
return state.map((user) => {
if (name === "alex") {
return { ...user, messages: [...user.messages, newMessage] };
}
return user;
});
Ideally, you would pass the name of the user in the payload of the action, so the following condition
if (name === "alex") { .. }
will become
if (name === action.payload) { .. }
I have used this npm package to solve this problem https://www.npmjs.com/package/immer. It is a super useful tool that helps reduce boilerplate code

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
}
*/

Delete an element from array using redux

I am trying to make a todo app using redux and I'm stack on how to delete a todo from the array.
reducer.js
export default function todo(state, action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
id: action.id,
text: action.text,
completed: false
}
case 'REMOVE_TODO':
return {
id: action.id,
...state.slice(id, 1)
}
default:
return state;
}
}
action.js
let nextTodoId = 0
export const addTodo = text => ({
type: 'ADD_TODO',
id: nextTodoId++,
text
})
export const removeTodo = id => {
type: 'REMOVE_TODO',
id
}
So far i can add and toggle a todo as completed or not. Thanks
Using redux you need to return all array elements except the removed one from reducer.
Personally, I prefer using the filter method of the Array. It'll return you a shallow copy of state array that matches particular condition.
case 'REMOVE_TODO':
return state.filter(({id}) => id !== action.id);
In react redux application, you should know, you always have to create a new object,
to deleting an item please use spread operator like this :
return [...state.filter(a=>a.id !== id)]

Updating state in reducer using variables

I'm build a simple app that expands and collapses sections of content based on their state. Basically, if collapse = false, add a class and if it's true, add a different class.
I'm using Next.js with Redux and running into an issue. I'd like to update the state based on an argument the action is passed. It's not updating the state and I'm not sure why or what the better alternative would be. Any clarification would be great!
// DEFAULT STATE
const defaultState = {
membership: 'none',
sectionMembership: {
id: 1,
currentName: 'Membership',
nextName: 'General',
collapse: false
},
sectionGeneral: {
id: 2,
prevName: 'Membership',
currentName: 'General',
nextName: 'Royalties',
collapse: true
}
}
// ACTION TYPES
export const actionTypes = {
SET_MEMBERSHIP: 'SET_MEMBERSHIP',
MOVE_FORWARDS: 'MOVE_FORWARDS',
MOVE_BACKWARDS: 'MOVE_BACKWARDS'
}
// ACTION
export const moveForwards = (currentSection) => dispatch => {
return dispatch({ type: actionTypes.MOVE_FORWARDS, currentSection })
}
// REDUCERS
export const reducer = (state = defaultState, action) => {
switch (action.type) {
case actionTypes.SET_MEMBERSHIP:
return Object.assign({}, state, {
membership: action.membershipType
})
case actionTypes.MOVE_FORWARDS:
const currentId = action.currentSection.id
const currentName = "section" + action.currentSection.currentName
return Object.assign({}, state, {
currentName: {
id: currentId,
collapse: true
}
})
default: return state
}
}
The currentName variable is causing an issue for the state to not update. I want to be able to dynamically change each sections state, which is why I thought I'd be able have a variable and update state like this.
It seems you can't use a variable for the key in the key/value pair. Why is this? What's an alternative to dynamically updating state?
That is because JavaScript understands that you want to create a key named currentName not a key with the value of the variable currentName. In order to do what you want, you have to wrap currentName in brackets:
return Object.assign({}, state, {
[currentName]: {
id: currentId,
collapse: true
}
})
So it will understand that the key will be whatever currentName is.
It also right:
return Object.assign({}, state, {
[currentName]: Object.assign({}, state[currentName], {
id: currentId,
collapse: true
})
})

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;
}
}

Resources