deep change state with variable index - javascript

I'm trying to follow the instructions found in Cleaner/shorter way to update nested state in Redux? [the accepted answer]
The thing is in my particular case, i have a layer that really depends on which model the user has selected up to that point. My structure:
Now in my reducer, i've tried (just want to change the favorite leaf to true):
let newStateForFavorites = {...state, models: {
...state.models,
62416: {
...state.models.62416,
details: {
...state.models.62416.details,
favorited: true
}
}
}};
Javascript complains. If i try to extract it to a var:
const currentModelToFav = action.payload.model_id;
let newStateForFavorites = {...state, models: {
...state.models,
currentModelToFav: {
...state.models.currentModelToFav,
details: {
...state.models.currentModelToFav.details,
favorited: true
}
}
}};
Javascript does not associate the const. And it errors out -currentModelToFav is undefined-
Ideas of what i'm doing wrong? Thanks!

If you want to use a variable as a property name you'll have to use these constructs:
{[variableName] : value}
and
{propertyName : variable[variableName]}
So, your object literal will become:
const currentModelToFav = action.payload.model_id;
let newStateForFavorites = {...state, models: {
...state.models,
[currentModelToFav]: {
...state.models[currentModelToFav],
details: {
...state.models[currentModelToFav].details,
favorited: true
}
}
}};

You need to use bracket notation to pull an object key using a variable:
const currentModelToFav = action.payload.model_id;
let newStateForFavorites = {
...state,
models: {
...state.models,
[currentModelToFav]: {
...state.models[currentModelToFav],
details: {
...state.models[currentModelToFav].details,
favorited: true
}
}
}
};

Related

Problems achieving required result of using the spread (...) operator with state object

I have a pimRegistration state initialization as shown in the chrome redux-devtools screen capture below. The nesting being referenced is pimRegistration (state.domain.patient):
I updated the patient.name object with the following spread operator statement:
store.update((state) => ({
...state,
...patientPath,
...{ [property]: value },
}));
...where property is the "name" property of the patient object with value. After the update, the following screenshot shows the new state:
Note that the original patient object (purple in the screenshot) is updated with the name object, duplicated and placed at the root of the state (yellow in screenshot).
I would like to overwrite the properties of the pimRegistration(state).domain.patient object, not to create a new patient object.
The state update is called as shown below.
store.update((state) => ({
...state,
...patientPath, // state.domain.patient
...{ [property]: value },
}));
I have tried my different combinations without achieving the desired result.
The complete update function is shown below.
update(property: string, path: string, value: any) {
const paths: string[] = path.split(".");
const pathReducer = (state: IRegistrationState, path_: string) => {
if (paths.length <= 0) {
return state.domain;
}
return state[path_];
};
const domainPath = state.domain;
let patientPath, nokPath, referrerPath;
if (path.includes("patient")) {
patientPath = paths.reduce(pathReducer, state);
}
if (path.includes("nok")) {
nokPath = paths.reduce(pathReducer, state);
}
if (path.includes("referrer")) {
referrerPath = paths.reduce(pathReducer, state);
}
store.update((state) => ({
...state,
...patientPath,
...{ [property]: value },
}));
}
The function above is invoked with the following statement in Angular 2.
if (this.path.includes("patient")) {
this._repo.update("name", "domain.patient", this.name);
}
Thanks
Deep updates to a store can be tricky. In your function you seem to be spreading the updates at the root rather than at the level you want the update at. This answer here outlines the usual practice to update the state. In short, something like
const newState = {
...state,
domain: {
...state.domain,
patient: {
...state.domain.patient,
[property]: value
}
}
}
Dynamically passing a path and updating this state can be… cumbersome. There are libraries that can help you do it such as immer, but you can possibly hack your way around with normal JS/TS.

The fastest way to find an element in an array with a given property name and replace it

I have a performance issue with NgRx, I have an array with thousands of objects that looks like this (I can't change that structure even I don't like it):
state.alarms structure:
[
{ global: {...} },
{ 282: {...} },
{ 290: {...} },
{ 401: {...} }
etc...
]
addNewAlarm(state, alarm) here alarm object is for example:
{ 282: {...} }
As you can see the object looks something like this { someNumber: nestedObjectForThatNumber }
I'm listening for changes and if some appear I have to replace object where "the key" is given number.
In the case from the screenshot for example I get { 282: {x: 1, y: 2, z: 3} } so I have to replace the item of array with index 1.
In my reducer I've created something like this but it doesn't work as I expected:
export function addNewAlarm(state: State, alarm: AlarmsObject): State | undefined {
const alarms: AlarmsObject[] = [...state.alarms];
if (state) {
const existingRecord = state.alarms.find(alarm1 => alarm1.hasOwnProperty(Object.keys(alarm)[0]));
if (existingRecord) {
const index = state.alarms.indexOf(existingRecord);
alarms[index] = alarm;
}
}
return { ...state, alarms };
}
Maybe someone can give me a hint how to do it in a right way?
you can use findIndex (If not found return -1) but, why not create an object?
stateObj: any = {};
this.state.forEach((x) => {
this.stateObj = { ...this.stateObj, ...x };
});
So you only need use
//Note you needn't return anything
addNewAlarm(stateObj: any, alarm: AlarmsObject){
const key=Object.keys(alarm)[0]
this.stateObj[key]=this.alarm[key]
}
A fool stackblitz

Javascript objects: How do I make the value of a key be another key (not the key's value)?

Is there a way to programmatically set the value of a key to another key? e.g.
export function getObjectForMapStateToProps(state, reducer) {
let stateObject = {};
for (let key in state[reducer]) {
if (state[reducer].hasOwnProperty(key)) {
stateObject[key] = state[reducer][key];
}
}
return stateObject;
}
The goal is for this to return an object where each key, value pair is like so:
namespace: state.city.namespace
What I am actually getting is the VALUE of the state.city.namespace key:
namespace: 'DIskieisSi98s8sjw'.
Thanks in advance for your help!
EDIT -- ADDING ADDITIONAL CONTEXT
#mem035 I probably should have added this context. I am trying create a function that will enable me to stop having to type out the mapStateToProps for Redux. For the above example, here is the map:
const mapStateToProps = state => {
console.log(getObjectForMapStateToProps(state, 'city'));
return {
namespace: state.city.namespace,
componentId: state.city.componentId,
items: state.city.items,
defaultValue: state.city.defaultValue,
selectItem: state.city.selectItem,
allowAdd: state.city.allowAdd,
validationRegEx: state.city.validationRegEx,
regExDescription: state.city.regExDescription,
hasForeignKey: state.city.hasForeignKey,
foreignKeyModel: state.city.foreignKeyModel,
foreignKeyValue: state.city.foreignKeyValue,
errorMessages: state.city.errorMessages,
isError: state.city.isError,
isLoading: state.city.isLoading,
apiRoute: state.city.apiRoute
};
};
When the value becomes a string none of the properties are mapping/working.
Just stringify your result instead of again accessing the object.
Instead of:
stateObject[key] = state[reducer][key];
Use:
stateObject[key] = `state.${reducer}.${key}`;

Destructing a javascript object to get inner object

I'm using the following code to get object from react state.
const { organizations } = this.state;
The sate object is as following.
this.state = {
userOrganizations: {},
OrganizationUsers: {}
}
userOrganizations is actually an object with an inner object named organizations. How can I map this using es6 code?
Edit
What I actually need is to get inner objects of both the userOrganizations and OrganizationUsers using the following code.
const { organizations, users } = this.state;
The organizations and users are sub objects which are inside the userOrganizations and OrganizationUsers.
So when I want to handle them, will they work with just calling
const { organizations, users } = this.state.userOrganizations, this.state.OrganizationUsers;
You can nest destructruring like
const { userOrganizations : { organizations } } = this.state;
or simply write
const { organizations } = this.state.userOrganizations;
This is so simple but still many got it wrong. Here is an example of inner destructuring
const obj = {
someArray: [ 1, 2, 3],
someInnerObj : {num: 123, txt: 'text'}
}
const {someArray: [first,second], someInnerObj: { num: myNum, txt: meText}} = obj
console.log(first,second,myNum,meText)
Try it in console
Just use dot notation until you get to the parent object just above the property you want
const obj = { outer: { inner: 'value' }};
const { inner } = obj.outer;
console.log(inner);
To destructure more than one thing at a time in different nested levels, try something like:
const x = {
state: {
userOrganizations: {
organizations: 'orgValue'
},
OrganizationUsers: {
users: 'userValue'
}
}
}
const { userOrganizations: { organizations }, OrganizationUsers: { users } } = x.state;
console.log(organizations + ' ' + users);
If i'am not wrong this should solve your issue,
const { organizations } = this.state.userOrganizations;

Change property in array with Spread Operator returns object instead of array

I want to change the property of an object similar to this, this is a simplified object with a few properties of the original:
state = {
pivotComuns: [
{
id: 1,
enabled : true
},
{
id: 2,
enabled : true
}
],
otherProperties : "otherProperties"
}
I'm changing the state of enabled like this:
state = {
...state,
pivotColumns: {
...state.pivotColumns,
[2]: {
...state.pivotColumns[2], enabled: !state.pivotColumns[2].enabled
}
}
}
It works, but instead of return an array like I is the pivotComuns property it returns an object, "notice that I change [] for {}":
state = {
pivotComuns: {
{
id: 1
enabled : true
},
{
id: 2,
enabled : true
}
},
otherProperties : "otherProperties"
}
What I'm doing wrong, I need to keep that property an array.
Very late post, but for future reference, you could do the following:
state = {
...state,
pivotColumns: state.pivotColumns.map(pc =>
pc.id === 2 ? {...pc, enabled:!pc.enabled} : pc
)
}
The advantage is that you will not change the object referenced in the "old array", you will instead insert a new object in its place. So if you would like to go back and forth in the state you can now do so.
example:
https://codepen.io/anon/pen/JyXqRe?editors=1111
I don't believe you can use the spread operator in such a way and in fact wouldn't recommend it if you could because it creates very hard to read code. There is a much simpler solution that I use on a daily basis when it comes to updating a key/value on an object where the value is an array:
var state = {
pivotColumns: [
{
id: 1,
enabled : true
}, {
id: 2,
enabled : true
}
],
otherProperties : "otherProperties"
}
var clonedPivotColumns = state.pivotColumns.slice();
clonedPivotColumns[1].enabled = !state.pivotColumns[1].enabled;
state = {
...state,
pivotColumns: clonedPivotColumns
}
this will get you the right results and will not cause any mutations.
working pen
http://codepen.io/finalfreq/pen/ggdJgQ?editors=1111

Categories