Update redux state in reactjs with input - javascript

In my application i am displaying person data.
I have a scenario where I can input new person. So far I have got this:
Reducer:
export default function (state = [], action) {
console.log("Reducing");
switch (action.type) {
case 'ADD':
console.log("ADDING!");
return action.payload;
break;
}
return state;
}
Action:
export const addPerson = (person) => {
console.log("addPerson Action Fired! ", person);
return {
type: 'ADD',
payload: person
}
};
I have a reducer with few person data which i am showing in my application but i am blocked at this point of adding new person. So far i can see my input in console log but can't figure out how can i add it to the state.
Help would be very much appreciated.
This is the reducer where i am showing the data from.
export default function () {
return ["abc","def","ghi","jkl"]
}
Now i am displaying a list of these elements from the array(each are one person). When i add one it will be added with these persons and show me the updated list.

You should do the following in your reducer:
export default function (state = [], action) {
console.log("Reducing");
switch (action.type) {
case 'ADD':
return [
...state,
action.payload,
]
}
return state;
}
In this way, you add the person to your existing array. Otherwise you changes your current state value from an array to an object.
Let's consider your person object value is the following and you dispatch it:
{
type: 'ADD',
payload: {
id: 1,
name: 'John Doe',
}
}
With your previous code, you would have the current value:
// First step
return action.payload;
// Second step
return { id: 1, name: 'John Doe' };
With the corrected code:
// First step
return [
...state,
action.payload,
]
// Second step
return [
...[],
{ id: 1, name: 'John Doe' },
]
// Third step
return [{ id: 1, name: 'John Doe' }];

Related

TypeError: can't access property Symbol.iterator, state[action.payload.savedVideos] is undefined

I have a problem with redux. I am working on an app where you can save a note about your youtube video. Everything is working fine, but when I started adding the 'ADD_ITEM' action I have the same mistake every time with this action in my reducer. The error I have is: TypeError: can't access property Symbol.iterator, state[action.payload.savedVideos] is undefined. I am using connect method and function mapDispatchToProps seems fine, so maybe the problem is in reducer or action creator. Here is my add item function in my reducer:
`
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_ITEM':
return {
...state,
[action.payload.savedVideos]: [...state[action.payload.savedVideos], action.payload.item],
};
here is my action creator:
export const addItem = (itemContent, savedVideos) => {
const getId = () => `_${Math.random().toString(36).substr(2, 9)}`;
return {
type: 'ADD_ITEM',
payload: {
savedVideos,
item: {
id: getId(),
...itemContent,
},
},
};
};
and my initial state:
const initialState = {
saves: [
{
id: 1,
title: 'Hello meow',
created: '18 may 2018',
link: 'https://www.youtube.com/watch?v=-QmyosHh-kU',
content: 'Nowy film gargamela!',
},
{
id: 2,
title: 'Hello meow',
created: '18 may 2018',
link: 'https://www.youtube.com/watch?v=-QmyosHa-kU',
content: 'Nowy film!',
},
{
id: 3,
title: 'Hello meow',
created: '18 may 2018',
link: 'https://www.youtube.com/watch?v=-QmyosHy-kU',
content: 'Nowy film gorgonzoli!',
},
{
id: 4,
title: 'Hello meow',
created: '18 may 2018',
link: 'https://www.youtube.com/watch?v=-QmyosHy-kU',
content: 'Nowy film gorgonzoli!',
},
{
id: 5,
title: 'Hello meow',
created: '18 may 2018',
link: 'https://www.youtube.com/watch?v=-QmyosHy-kU',
content: 'Nowy film gorgonzoli!',
},
],
};
https://redux.js.org/tutorials/fundamentals/part-3-state-actions-reducers#creating-the-root-reducer
Assuming savedVideos is from the initialState, when u add video, you are adding the following object, and you want to add on to your initialState (aka initial store), you do not need to pass savedVideos to actions and reducer.
{
id: 1,
title: 'Hello meow',
created: '18 may 2018',
link: 'https://www.youtube.com/watch?v=-QmyosHh-kU',
content: 'Nowy film gargamela!',
},
Your action can be..
export const addItem = (itemContent, savedVideos) => {
const getId = () => `_${Math.random().toString(36).substr(2, 9)}`;
return {
type: 'ADD_ITEM',
payload: {
id: getId(),
...itemContent,
},
};
};
Your reducer can be
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_ITEM':
return { ...state, saves: [...state.saves, action.payload] }
//or whatever new item u need to add to the state (store).
case 'DELETE_ITEM': //bonus
return { ...state, saves: state.saves.filter( x => x.id !== action.payload.id) }
}
I want to explain the error
"TypeError: can't access property Symbol.iterator, state[action.payload.savedVideos] is undefined"
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_ITEM':
return {
...state,
[action.payload.savedVideos]: [...state[action.payload.savedVideos], action.payload.item],
};
What you are actually doing in your reducer is updating a property name from a variable.
Square brackets around a property name means that it's a variable.
The property name is the value of action.payload.savedVideos. But that's not a valid property name because action.payload.savedVideos is an array (a Symbol.iterator).
You don't need a dynamic property name because you are always updating the property saves. As explained by #Someone Special, you don't need to include the existing savedVideos in your action either because you can already access them from the state.
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_ITEM':
return {
...state,
saves: [...state.saves, action.payload.item],
};

Array inside object with store

I have this Redux store and reducer
const INITIAL_WORK = {
departments: []
}
const works = (state = INITIal_WORK, action) => {
switch(action.type){
case 'ADD_DEPARTMENT':
return {
...state,
departments: [...state.department, action.item]
}
default:
return state
}
}
In departments work people so I want to this people was inside a single department works people. So after fetch data from db I want my store look like this:
const INITIAL_WORK = {
departments: [
{
id: 1,
name: "First department",
people: [
{
id: 1,
name: "Johna Wayne"
},
{
id: 2,
name: "Jessica Biel"
},
{
id: 3,
name: "Bratt Pitt"
}
]
},
{
id: 2,
name: "second department",
people: [
{
id: 4,
name: "Salma Hayek"
},
{
id: 5,
name: "Sylvester Stallone"
}
]
}
]
}
Is it possible create case in reducer which will be added people inside people array inside single department? How can I do that?
Yes, absolutely.
Let us assume your API returns payload as following.
{
departmentId: `id of the department`
id: `person id`
name: `person name`
}
const INITIAL_WORK = {
departments: []
}
const works = (state = INITIal_WORK, action) => {
switch(action.type){
case 'ADD_DEPARTMENT':
return {
...state,
departments: [...state.department, action.item]
}
case 'ADD_PERSON':
const { departmentId, id, name } = action.item
const departments = [...state.departments];
departments[departmentId].people.push({id, name})
return {
...state,
departments
}
default:
return state
}
}
Combining Immer.js and Redux will be helpful for this case.
Here is a simple example of the difference that Immer could make in practice.
// Reducer with inital state
const INITAL_STATE = {};
// Shortened, based on: https://github.com/reactjs/redux/blob/master/examples/shopping-cart/src/reducers/products.js
const byId = (state = INITAL_STATE, action) => {
switch (action.type) {
case RECEIVE_PRODUCTS:
return {
...state,
...action.products.reduce((obj, product) => {
obj[product.id] = product
return obj
}, {})
}
default:
return state
}
}
After using Immer, our reducer can be expressed as:
import produce from "immer"
// Reducer with inital state
const INITAL_STATE = {};
const byId = produce((draft, action) => {
switch (action.type) {
case RECEIVE_PRODUCTS:
action.products.forEach(product => {
draft[product.id] = product
})
}
}, INITAL_STATE)
let mergePeople=[];
INITIAL_WORK.departments.filter((cur,index)=>{
cur.people.filter((person)=>{
mergePeople.push(person);
})
}
)
console.log(mergePeople);

React redux - updating nested array in state

I'm trying some app in react redux and i have a problem with updating (push, remove, update) the nested array in state.
I have some object called service like this:
{
name: 'xzy',
properties: [
{ id: 1, sName: 'xxx'},
{ id: 2, sName: 'zzz'},
]
}
Whatever I did (in case of adding property to collection) in the reducer with the properties collection generate problem that all properties got same values as the last I had recently added -> Added property object is in service properties collection but the action replace all values in all properties in this collection.
My reducer:
export function service(state = {}, action) {
switch (action.type) {
case 'ADD_NEW_PROPERTY':
console.log(action.property) // correct new property
const service = {
...state, properties: [
...state.properties, action.property
]
}
console.log(service); // new property is pushed in collection but all properties get same values
return service
default:
return state;
}
}
I have tried some solution with immutability-helper library and it generate the same problem:
export function service(state = {}, action) {
case 'ADD_NEW_PROPERTY':
return update(state, {properties: {$push: [action.property]}})
default:
return state;
}
For example when I add new property { id: 1, sName: 'NEW'} to example above I will get this state:
{
name: 'xzy',
properties: [
{ id: 1, sName: 'NEW'},
{ id: 1, sName: 'NEW'},
{ id: 1, sName: 'NEW'}
]
}
Can someone help? :)
Make a copy of action.property as well. Whatever is dispatching this action, it could be reusing the same object.
export function service(state = {}, action) {
switch (action.type) {
case 'ADD_NEW_PROPERTY':
console.log(action.property) // correct new property
const service = {
...state,
properties: [
...state.properties,
{ ...action.property }
]
}
console.log(service); // new property is pushed in collection but all properties get same values
return service
default:
return state;
}
}
I'd recommend you to use Immutable data https://facebook.github.io/immutable-js/docs/#/List
import { fromJS, List } from 'immutable';
const initialState = fromJS({
propeties: List([{ id: 1, sName: 'xyz' }]
}
function reducer(state = initialState, action) {
case ADD_NEW_PROPERTY:
return state
.update('properties', list => list.push(action.property));
// ...
}
Your service reducer should probably look somewhat like this:
// Copy the state, because we're not allowed to overwrite the original argument
const service = { ...state };
service.properties.append(action.property)
return service
You should always copy the state before returning it.
export default function(state = {}, action) {
switch(action.type) {
case 'GET_DATA_RECEIVE_COMPLETE': {
const data = action.firebaseData;
const newState = Object.assign({}, state, {
data
});
return newState
}
default:
return state;
}
}

Updating object inside redux state throws an error

I'm trying to update array inside object in my reducer.
const initialState = {
group: {
name: "",
date: "",
description: "",
users: [],
posts: []
},
morePosts: false,
groups: []
};
export function groups(state = initialState, action) {
switch (action.type) {
.......
case REQUEST_MORE_POSTS:
{
return {
...state,
group:{
...state.group,
posts: [
...state.group.posts,
...action.payload.posts
]
},
morePosts: action.payload.morePosts
}
}
case ADD_NEW_POST:
{
return {
...state,
group:{
...state.group,
posts: [
action.payload,
...state.group.posts
]
}
}
}
........
default:
return state;
}
}
Unfortunately in both cases I get an error:
It works when I extract posts out of my group object but I need it inside.
I can't figure out what I've done wrong here. Can someone point me to the right direction?
Here is an action creator for adding new post.
export function addPost(url, payload) {
return function(dispatch) {
axios.post(url + "php/addPostGroup.php", {payload}).then(response => {
dispatch({
type: ADD_NEW_POST,
payload: response.data.post
})
})
}
}
response.data.post is a simple object.
I've added console.log() before dispatch. This is how my response looks like:
Alright I solved it. Before fetching any data my state.group.posts array was for some reason treated as undefined. I had to manually declare it as empty array using
var posts = state.group.posts != undefined ? state.group.posts:[];

My delete operation is not working properly?

This is my delete code, it works only on first invokation?
const byId = (state = {}, action) => {
switch (action.type) {
case 'REMOVE_TODO':
return Object.keys(state).filter( key => {
return state[key].id !== action.id
}).reduce((acc,key) => Object.defineProperty(acc, key, {
value: state[key], }),{});
default:
return state;
}
};
My state object is like this:
{"abcd1": {id: "abcd1", text: "hello world"}, "efgh2": {id: "efgh2", text: "hello there"}
So what happens is I can delete on the first invokation, then my object becomes all empty when I try to invoke again? or only {} . Can't figure out why?
Well I would prefer to shape my state object like:
[{id: "abcd1", text: "hello world"},{id: "efgh2", text: "hello there"}]
since it's just redundant to repeat the object key and the todo id if they are the same.
Then, in the reducer you can have something like:
const byId = (state = [], action) => {
switch (action.type) {
case 'REMOVE_TODO':
return state.filter((todo) => todo.id != action.id)
default:
return state;
};

Categories