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

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.

Related

VueJS : how to set a default value of a key / value in a prop of type array?

I'm learning Vue JS and I am creating my components.
I'm stuck on an issue.
I would like to have my component taking in params an array of object like that :
data() {
return {
items: [
{
text: 'Admin',
href: '#'
},
{
text: 'Manage',
href: '#'
},
{
text: 'Library',
active: true
}
]
}
So, I tried to implement my component with props :
props: {
items: {
type: Array,
required: true
}
However, I have no idea how to say that : if items does not contains active key, then it should have a false default value.
If you have any advices/links or explanations, I will be very grateful to you.
You can make a computed property which will fill-in those active: false by default, and override it with the provided items:
props: {
items: {
type: Array,
required: true
}
},
computed: {
normalizedItems() {
return this.items.map(x => ({ active: false, ...x }));
}
}
You would then use normalizedItems in your template instead of items

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

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

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

Update the state of object in reducer

I am new to react js. Here, I do have a functionality where there are three diff rows that can be added or removed. I have a reducer which is like,
const initialState = {
Low: [
{
id: 'L0',
technologyId: 0,
technology: '',
type: '',
count: '',
level: 'EASY'
}
],
Medium: [
{
id: 'M0',
technologyId: 0,
technology: '',
type: '',
count: '',
level: 'Medium'
}
],
High: [
{
id: 'H0',
technologyId: 0,
technology: '',
type: '',
count: '',
level: 'Tough'
}
],
SO, Here, I want to have this as a generic,so, I am trying to add objects on click of the plus and remove on - . So,
Here, I am adding using ,
const tempObj = {
id: tempvar + id,
technologyId: 0,
technology: temp[0].technology,
type: type,
count: '',
level: addtype
}
I create this object now depend upon the addType I add it in that particular array.
I do it using this way,
const addData = [...this.props.lowData[addtype], tempObj];
this.props.addNewTech(addData);
So, Here this ...this.props.lowData[addtype] returns an object not an array, is this the problem ? If yes, It should return an array not the object but how is this returning the object and not an array.
My props.lowData is like ,
an object with three different arrays, I mean same as that of the reducer .,But every time I add it adds that element in the Low array only and not in the medium or high. Can any one help me with this ?
The problem is in the reducer you are only updating the Low property. You need to check what property needs to update. For that you can pass addtype to your action creator like so
this.props.addNewTech({ addData, addtype });
And then in action creator
export function addNewTech(data) {
return {
type: ADD_NEW,
data //is equavalent to data: data (ES6)
}
}
And then in reducer dynamically update the object
case ADD_NEW:
return {
...state,
//here choose the property to be updated dynamically
//replace Low: action.data with below line
[action.data.addtype]: action.data.addData,
error: false,
}
**Note here [action.data.addtype]: action.data.addData we are using computed property names in ES6.

React.Element object internals are not shown when serialized

I can have a functional component like this:
const FuncList = ({ name }) => {
return (
<div className="shopping-list">
<h1>Shopping List for {name}</h1>
<ul>
<li>Instagram</li>
<li>WhatsApp</li>
<li>Oculus</li>
</ul>
</div>
)
}
I can instantiate it to see basically the entire element content:
const obj = FuncList({ name: 'Pete' })
console.log(JSON.stringify(obj, null, 4))
-----
{
'type': 'div',
'key': null,
'ref': null,
'props': {
'className': 'shopping-list',
'children': [
{
'type': 'h1',
'key': null,
'ref': null,
'props': {
'children': [
'Shopping List for ',
'mike'
]
},
'_owner': null,
'_store': {}
},
{
'type': 'ul',
'key': null,
'ref': null,
'props': {
'children': [
{
'type': 'li',
'key': null,
'ref': null,
'props': {
'children': 'Instagram'
},
'_owner': null,
'_store': {}
},
....
]
},
'_owner': null,
'_store': {}
}
]
},
'_owner': null,
'_store': {}
}
Now when I use it with JSX syntax this:
const obj = <JSXList name="Pete" />;
// same as: const obj = React.createElement(JSXList, { name: "Pete" });
console.log(JSON.stringify(obj, null, 4))
I get almost empty object. Where is the link to JSXList stored? Where are all the properties? Are they hidden inside the object somehow? It looks like 'return' method of JSXList was never called, thus it was never expanded, but why there's no reference to it?
{
'key': null,
'ref': null,
'props': {
'name': 'Pete'
},
'_owner': null,
'_store': {}
}
If I would use 'div' instead of JSXLint as a first argument, I would get at least 'type' property which would indicate the purpose of the element. Is there any design rationale behind that?
const obj = React.createElement('div', { name: "Pete" });
console.log(JSON.stringify(obj, null, 4))
Is there any point in hiding that reference? It sort of obscure object introspection, as far as I can tell.
You can use codepad to play around if you like.
I get almost empty object. Where is the link to JSXList stored?
In obj.type. You're not seeing type in the log because functions are not serializable, so JSON.stringify skips them.
Where are all the properties?
It's there. You only gave it one property: name: 'pete'. If you're expecting to see divs and such, the reason you're not is that the following two lines are not equivalent:
const obj = FuncList({ name: 'Pete' })
const obj = <FuncList name="Pete" />;
As you pointed out in a comment in your code, the jsx expands to this, which again is not equivalent to calling FuncList:
React.createElement(FuncList, { name: "Pete" });
React.createElement creates a small object describing what to render, which is the small object you're seeing. What normally happens next is that react's reconciliation algorithm decides whether it needs to take things farther. If it does, then only then will it instantiate the FuncList, calling FuncList({ name: 'Pete' }) and producing the larger object.

Categories