Reducer in React Shows: Cannot convert undefined or null to object - javascript

I needed to convert some data in my reducer from an array to object. So I used Object.keys to do so. But I started to get this error that it cannot convert undefined.
After troubleshooting for quite a bit I have reduced the code in my reducer to its simplest form as below and I'm still seeing the same error. Can someone please advise what is the issue here? Ofcourse now that I have removed Object.keys I'm seeing a slightly different error but the main issue seems to be the same. The object in the state is undefined.
Thank you!
Just to add something when I do a console.log(state.collections["hats"]);
right before the switch statement, it shows the data as expected i.e the object/value for the "hats" key
TypeError: Cannot read property 'hats' of undefined
My simplified Reducer code:
import SHOP_DATA from '../../data/shop.data.js';
const INITIAL_STATE = {
collections: {
hats:{
id: 1,
title: 'Hats',
routeName: 'hats',
items: [
{
id: 1,
name: 'Brown Brim',
price: 25
},
{
id: 2,
name: 'Blue Beanie',
price: 18
}
]
},
sneakers: {
id: 2,
title: 'Sneakers',
routeName: 'sneakers',
items: [
{
id: 10,
name: 'Adidas NMD',
price: 220
},
{
id: 11,
name: 'Adidas Yeezy',
price: 280
}
]
}
}
}
const collectionsReducer = (state= INITIAL_STATE, action) => {
switch(action.type) {
default:
return ["hats", "sneakers"].map(key => state.collections[key]);
}
}
export default collectionsReducer;

Change the reducer logic like below then it will work
switch(action.type) {
default:
return ["hats", "sneakers"].map(key =>INITIAL_STATE['collections'][key])
}
Thank you

there is no key named sneakers, thats why it is returning undefined

Related

How do I access properties of objects inside another object

I have an api endpoint form where I am getting data like below. How i will access the values title, short_title etc.
blog: {
paginations: true,
isLoading: false,
particularBlog: [],
count: 13,
next: 'http://127.0.0.1:8000/api/blog/all-blog/?page=2',
previous: null,
results: [
{
id: 47,
user: 1,
title: 'adasd',
short_title: 'asd',
publish: '2019-09-16',
slug: 'adasd',
comments_count: 0,
likes_count: 0
},
{
id: 46,
user: 1,
title: 'adasda',
short_title: 'asdas',
publish: '2019-09-16',
slug: 'adasda',
comments_count: 0,
likes_count: 0
}
]
},
what i have done is
<div>{
this.props.blog && Object.keys(this.props.blog).map((key) => {
return <p>{this.props.blog.results[key]}</p>
})
}</div>
but it is giving error stating paginations' of undefined. Can someone please pointout what i am doing wrong here?
Where is what is happening
Object.keys(this.props.blog).map((key) => { is getting the keys of this.props.blog and this.props.blog.results[key] is trying to access the properties of results with the keys of blog.
What you should do is have another .map with Object.keys of this.props.blog.results
OR
What I think you are trying to do is list all properties on the this.props.blog.results array, so here is what you can do
this.props.blog && this.props.blog.results && this.props.blog.results.map(result=> <p>{result.short_title}</p>
You do .map on the results array and display short_title.
blog.results.map(el => (<div>{el.short_title}</div>))

How to translate object data to a format known by react-d3-tree

I am currently having problems using my data in react-d3-tree. The data coming from the server is not compatible with the format that react-d3-tree accepts.
I was told that doing the process using a foreach function for every item will work, but I think that will be really slow, especially whe the data is too big.
The data (as shown in my console) right now is like this:
0: {
series: 'Fate'
type: 'Servant'
class: 'Saber',
},
1: {
series: 'Fate',
type: 'Servant'
class: 'Archer',
},
2: {
series: 'Fate',
type: 'Demi-servant'
class: 'Shielder',
}
And I want to achieve this structure:
[
{
name: 'Fate',
children: [
{
name: 'Servant',
children: [
{
name: 'Saber',
children: []
},
{
name: 'Archer',
children: []
}
]
},
{
name: 'Demi-servant',
children: [
{
name: 'Shielder',
children: []
}
]
}
]
}
]
This is only sample data, and later on, and I might be converting data with more children. Is there any npm package that can be helpful?
Nevermind, I actually found my answer.
I'll share these useful links in case anyone finds this question too.
https://www.npmjs.com/package/shape-json
https://www.npmjs.com/package/shape-array

Add object to nested array

I have initial state as
sites = [
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
];
I'm trying to add a vehicle to a given site when selected from a list which is in a component SiteVehcleSelection and the method that handles the selection is:
handleVehicleSelection = (event) => {
const vehicle = this.props.vehicles.find((v) => v.id === parseInt(event.target.dataset.id, 10));
this.props.handleVehicleSelection(event, this.state.site.id, {...vehicle});
};
which passes it up to parent SiteList method:
handleVehicleSelection = (event, siteId, vehicle) => {
this.props.dispatch(siteActions.handleVehicleSelect(siteId, vehicle), event.target.checked);
}
called from the SiteList class:
export function handleVehicleSelect(siteId, vehicle, cmd){
return (dispatch) => {
debugger;
return fetch(`${BASE_URL}/accounts/site-vehicle-action/${siteId}/${vehicle.id}/${cmd}`, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: ''
}).then((res) => {
return res.json();
}).then((json) => {
if (json.msg === true) {
dispatch(vehicleSelect(siteId, vehicle));
}
});
}
}
which dispatches to this:
export function vehicleSelect(siteId, vehicle){
return {type: actionTypes.ADD_VEHICLE_TO_SITE, siteId, vehicle};
}
and my reducer is:
case actionTypes.ADD_VEHICLE_TO_SITE:
debugger;
const siteIndex = state.findIndex((site) => site.id === action.siteId);
console.log(state);
const newState = [...state.slice(0, siteIndex), {...state[siteIndex], vehicles: [...state[siteIndex].vehicles, action.vehicle],}, ...state.slice(siteIndex +1)];
console.log(newState);
return newState;
when I log before and after the changes have taken place, the vehicle has been added to the correct site but it does not show/refresh in the view here is the logging of the state before and after.
Before change :
0: {drivers: Array(0), id: 1, name: "Site One", vehicles: Array(0)}
1: {drivers: Array(0), id: 2, name: "Site Two", vehicles: Array(0)}
2: {drivers: Array(0), id: 3, name: "Site Three", vehicles: Array(0)}
length: 3
__proto__: Array(0)
After change:
0: {drivers: Array(0), id: 1, name: "Site One", vehicles: Array(1)}
1: {drivers: Array(0), id: 2, name: "Site Two", vehicles: Array(0)}
2: {drivers: Array(0), id: 3, name: "Site Three", vehicles: Array(0)}
length: 3
__proto__: Array(0)
can see the first one had the vehicle added correctly and this is the new state but nothing happens on return as if sitesList does not refresh.
Hope this edit helps explain more.
I think below code will shed some light. I assume you have the corresponding indexes.
let state = [
{ id: "id1", name: "name1", items: [{},{}] },
{ id: "id2", name: "name2", items: [{},{}] },
]
function reducer() {
switch("Action") {
case ADD_SITE: { // add new element to state
return [
...state,
payload.site,
]
}
case ADD_SITE_AT_INDEX: { // add new element to state at index: idx
return [
...state.slice(0, idx),
payload.newSite,
...state.slice(idx)
]
}
case ADD_ITEM: { // add new item to site with index: idx
return [
...state.slice(0, idx),
{
...state[idx],
items: [
...state[idx].items,
payload.newItem
],
},
...state.slice(idx+1)
]
}
case ADD_ITEM_AT_INDEX: { // add new item to site with index: idx, at item index: item_idx
return [
...state.slice(0, idx),
{
...state[idx],
items: [
...state[idx].items.slice(0, item_idx),
payload.newItem,
...state[idx].items.slice(item_idx),
],
},
...state.slice(idx+1)
]
}
}
}
let say this is your structure of state with keys (i am giving random name to the keys so i can explain)
{
data:[{id:"some_id",name:"some_name",items1:[{},{}]}]
}
//suppose this is your reducer code.
case ADD_ITEM:
return{
...state,
data: state.data.map(val=>{
if(val.Id===action.payload.Id){
return{
...val,
items1:[...val.items1,action.payload.data]
}
}
return{...val}
})
}
here you'll be sending Id and data from the action like:
{type:"ADD_ITEM",payload:{Id,data}}
where id will be the id of first level object which's array needs to be updated,
and data will be the data you want to add into the array..
If you just want to add an object to an array with a given structure, you can do this:
Structure
[
{ id, name, items1[{object},{object}]
]
copy an existing state and then add a new object to the end of the array.
return {
...state,
items1: state.items1.concat(action.newObject),
};
or with ES6 spread
return {
...state,
items1: [...state.items1, action.newObject],
};
// state: { id, name, items1: [{object},{object},{newObject}] }

Targeting Specific Array Element with Assignment Destructuring

I am using some assignment destructuring in my MongoDB/Node backend in order to handle some post-processing. I'm just trying to understand how this destructuring works, and if, in the case of an array of multiple elements and nested arrays, if I can input the element I want to target.
Take for instance this code:
services: [
,
{
history: [...preSaveData]
}
]
} = preSaveDocObj;
My assumption is that the "," in "services" for the above code will default to looking at the first element in the array. Correct?
Now, if I have a document structure that looks like this (see below), and I know I want to target the "services" element where "service" is equal to "typeTwo", how would I do that?:
{
_id: 4d39fe8b23dac43194a7f571,
name: {
first: "Jane",
last: "Smith"
}
services: [
{
service: "typeOne",
history: [
{ _id: 121,
completed: true,
title: "rookie"
},
{ _id: 122,
completed: false,
title: "novice"
}
]
},
{
service: "typeTwo",
history: [
{ _id: 135,
completed: true,
title: "rookie"
},
{ _id: 136,
completed: false,
title: "novice"
}
]
}
]
}
How can I edit this code (see below) to specifically target the "services" array where "service" is equal to "typeTwo"?
services: [
,
{
history: [...preSaveData]
}
]
} = preSaveDocObj;
Don't overdestructure, just find:
const { history: [...preSavedData] } = doc.services.find(it => it.serice === "typeTwo");

How to modify a specific element of a nested object using ES6?

I have this initialStore:
const initialState =
{
0:{
screen_name: '1',
props: null,
},
1:{
screen_name: '2',
props: null,
},
2:{
screen_name: '3',
props: null,
},
3:{
screen_name: '4',
props: null,
},
4:{
screen_name: '5',
props: null,
},
}
I want to know how can I modify for example the value state.0.screen_name: 1, while keeping the rest of the original state using ES6?
This is the approach that I have so far:
export const navigationReducer = createReducer(initialState, {
[types.CHANGE_SCREEN_EXPERIMENTAL](state,action){
return{...state[action.id], screen_name:action.screen_name, ...state
};
}
},
);
However, that returns this:
The action.id is 0, it should modify the state[0] element, however, it copied its elements, modified them and placed them on the state object itself instead of the state[0] object.
Desired result:
navigationReducer:{
0: {
props: null,
screen_name: "ad"
}
1:Object
2:Object
3:Object
4:Object
}
I do not want the screen_name nor props to be outside those objects (0,1,2,3,4).
Any ideas are well appreciated.
I found the way to do it using ES6
Here is how:
export const navigationReducer = createReducer(initialState, {
[types.CHANGE_SCREEN_EXPERIMENTAL](state,action){
return{
...state,[action.id]:{
...state[action.id],
screen_name:action.screen_name,
props:action.props,
}
};
}
},
);
This results in:
Thank you very much for your help and time #ibrahimmahrir.

Categories