ESLint: Assignment to property of function parameter - javascript

Given a structure like:
products: {
'123': {
details: [
{
price: 45,
unitPrice: 0,
productName: 'apples'
}
]
}
}
And a function,
function modifyPriceForApples(coefficient) {
const products = cloneDeep(vehicleProductSelector.getAllProducts(store.getStore().getState()));
let appleProduct;
Object.keys(products).forEach((productId) => {
const {
details
} = vehicleProducts[productId];
details.forEach((detail) => {
const {
price,
productName
} = detail;
if (productName === 'apples') {
detail.unitPrice = coefficient * detail.price;
appleProduct = products[productId];
}
});
});
return appleProduct;
}
I am getting linting error: Assignment to property of function parameter
How can I resolve that, barring disabling the linting rule?
I keep seeing array destructuring as the answer to this problem, however I'm not too sure what that would look like in practice given that this is a pretty complex structure.

Instead of of using forEach, use a map and act as though arguments provided to a function are immutable. It's mad that you are doing details.forEach((detail) => {...}); and then assigning to detail with detail.unitPrice = coefficient * detail.price;.

Related

How to update new object into the existing object in ReactJS?

I have one object named waA which is required in final step. But in ReactJS the new object is not getting updated in the previous object using switch in a function.
The object code is given below:
waA = {
jsonObject: {
wAConfigur: {}
}
}
var updatewaA = waA.jsonObject.wAConfigur;
The button has onClick event and its function is:
function next(){
switch(case){
case 1:
const bD = {
'name': 'john',
'title': 'Boy',
}
updatewaA.configureSet = bD;
break;
case 1:
const cE = {
'age': 34,
}
updatewaA.newDate = bD;
break;
}
}
The final Object which is needed is:
{
jsonObject: {
wAConfigur: {
configureSet: {
'name': 'john',
'title': 'Boy',
},
newDate: {
'age': 34
}
}
}
}
But for some reason in ReactJS the object is not getting updated.
How can I be able to do it? Thank you.
You need to store the object inside the react state to later make it update and render new state to the dom.
import {useState} from "react"
// new state waA and updater function setWaA
const [waA, setWaA] = useState({
jsonObject: {
wAConfigur: {},
},
})
function next(number){
switch(number){
case 1:
const bD = {
'name': 'john',
'title': 'Boy',
}
// here we added configureSet
setWaA(prevWaA => {
...prevWaA,
jsonObject: {
...prevWaA.jsonObject,
configureSet = bD
}
})
const cE = {
'age': 34,
}
// here we added newDate
setWaA(prevWaA => {
...prevWaA,
jsonObject: {
...prevWaA.jsonObject,
newDate = cE
}
})
break;
}
}
Continuing on the answer given by Abhishek, in ReactJS you should take care of using objects and arrays in a functional way, keeping them constants throughout the entire application scope so that you don't change your variables during usage.
This ensures that each step the variable is used in is "new" and unchanged, rather, cloned from the previous value without actually changing the previous one. It's also best because objects and arrays are references in memory and are not actual values, hence if you assign an object or array, you are actually assigning the reference, not the value of it.
With React you can spread the object or array to clone it but take care of the object because it's not a deep clone like Lodash would do, so you have to do as specified by Abhiskek.
import { useState } from 'react'
function Task() {
const [config, setConfig] = useState(initialObjectConfig)
useEffect(() => {
setConfig(prevWaA => {
...prevWaA,
jsonObject: {
...prevWaA.jsonObject,
newDate = cE
}
})
}, [])
}
You can apply the same thought to a Switch statement but if you don't want to keep state immutability you can take a look at immer.js which is a library that can help you achieve exactly what you are looking for.

Javascript destructuring and function as an object

Could someone possibly help me understand this function and destructuring a little better?
export default ({ names }) => {
const {
people: {
children = []
} = {}
} = names;
children.find((child) => child.email);
};
I understand that names is being destructured to extract data stored in objects in order to find the first child in the children's array that has an email address. But what I don't understand is why is this function declared like this ({ names })? Is it because names is an object? Sorry if this is a silly question but I am struggling to understand this.
Lets break it down:
Your function takes in 1 parameter which is an object...
...that object must have a property names.
Then the function destructures the object as follows (const { people: { children = [] } = {} } = names;):
First, you destructurize a property called people from the names argument
If people doesn't exist, it takes the default value of {}.
And finally, it grabs the property children (which are an array of
objects) from its parent people.
Next, the function logic with .find()
All it does is searching for a child from children from people from names from the argument object that has a property email...
...and returns it. Unfortunately that part is missing in your function code.
So in your snippet, the function actually does absolutely nothing, except to be unnecessarily complicated :P
To wrap things up. This is what your argument to the function could look like:
const argument = {
names: {
people: {
children: [{ email: "myemail#mail.com", name: "thechildtobefound" }],
}
}
};
And here's a working sample to try:
const func = ({ names }) => {
const { people: { children = [] } = {} } = names;
return children.find((child) => child.email);
};
const argument = {
names: {
people: {
children: [{ email: "myemail#mail.com", name: "thechildtobefound" }],
},
},
};
console.log(func(argument));

How to search nested object by following JSLint

I have my object structured as below and I want to find the product with provided ID.
0 :{
id: 0,
title: 'xxxx',
url: "www.test.com"
quantity: 100
},
1 :{
id: 10,
title: 'xxxx',
url: "www.test.com"
quantity: 100
},
// and so on...
In order to search nested attribute within the object, I have written the below function:
export const selectProductById = (state, productId) => {
const obj_index = Object.keys(state.products).find(function(idx) {
if (state.products[idx].id == productId) {
return idx;
}
}
return state.products[obj_index]
}
This works but I will always get a warning during compilation of my react app.
Expected '===' and instead saw '=='
But if I change this into === the code will not work anymore, does anyone knows how to change this so that it follows JSLint rules ?
It sounds like the productId is not a number. Cast it to a number first:
if (state.products[idx].id === Number(productId)) {
But you should return a truthy or falsey value from the .find callback, not something that you're iterating over (since you may not be sure whether it's truthy or falsey, and it's potentially confusing). Return the result of the === comparison instead:
const { products } = state;
const obj_index = Object.keys(products).find(
key => products[key].id === Number(productId)
);

Is destructuring this possible?

I am new to destructuring and need help finding the best solution. I have a pretty complicated object coming back as a response and would like to clean it up. Without doing anything, it looks like this:
const homeTeam = {
totalPlaysFor: res.data.stats.home.teamStats[0].miscellaneous.offensePlays,
totalPlaysAgainst: res.data.stats.away.teamStats[0].miscellaneous.offensePlays
}
I know I can do something like:
const { offensePlays } = res.data.stats.home.teamStats[0].miscellaneous;
but that only solves my problem for one of them and would leave the other still long and tough to read.
You can destructure the stats property in advance, then make a helper function that takes the .home or .away object and navigates to the nested .offensePlays:
const { stats } = res.data;
const getOffPlays = obj => obj.teamStats[0].miscellaneous.offensePlays;
const homeTeam = {
totalPlaysFor: getOffPlays(stats.home),
totalPlaysAgainst: getOffPlays(stats.away)
};
Without having a standalone helper function, you could also create the object by .mapping an array of properties (eg [['totalPlaysFor', 'home'], ['totalPlaysAgainst', 'away']]) and pass it to Object.fromEntries, but that would be significantly less readable IMO.
While you can destructure this directly, via something like
const {
data: {
stats: {
home: {
teamStats: [{
miscellaneous: {
offensePlays: totalPlaysFor
}
}]
},
away: {
teamStats: [{
miscellaneous: {
offensePlays: totalPlaysAgainst
}
}]
}
}
}
} = res
const homeTeam = {totalPlaysFor, totalPlaysAgainst}
that feels pretty ugly.
Regardless whether you think your code is ugly, I can see a much more important problem with it: it doesn't work when any of those properties doesn't exist. To solve that, you might want a feature not yet ubiquitous in the language, one of Optional Chaining that would avoid throwing errors for a nested path such as 'data?.stats?.home?.teamStats?.0?.miscellaneous?.offensePlays', simply returning undefined if one of the nodes mentioned does not exist.
An equivalent feature is available as a function in many libraries. Underscore's property, lodash's property, and Ramda's path offer slightly different versions of this. (Disclaimer: I'm a Ramda author.) However it's easy enough to write our own:
const getPath = (pathStr) => (obj) =>
pathStr .split ('.')
.reduce ((o, p) => (o || {}) [p], obj)
const res = {data: {stats: {home: {teamStats: [{miscellaneous: {offensePlays: "foo"}}]}, away: {teamStats: [{miscellaneous: {offensePlays: "bar"}}]}}}}
const homeTeam = {
totalPlaysFor: getPath ('data.stats.home.teamStats.0.miscellaneous.offensePlays') (res),
totalPlaysAgainst: getPath ('data.stats.away.teamStats.0.miscellaneous.offensePlays') (res)
}
console .log (homeTeam)
Note that the array is not delineated in this simple version as [0] but just as .0. Obviously we could make this more sophisticated if necessary.
You can destructure as much as you want!
const homeTeam = {
totalPlaysFor: res.data.stats.home.teamStats[0].miscellaneous.offensePlays,
totalPlaysAgainst: res.data.stats.away.teamStats[0].miscellaneous.offensePlays
}
You can even use a combination of array destructuring / object destructuring here:
const [ home ] = res.data.stats.home.teamStats
const [ away ] = res.data.stats.away.teamStats
const { offensePlays: totalPlaysFor } = home.miscellaneous
const { offensePlays: totalPlaysAgainst } = away.miscellaneous
const hometeam = { totalPlaysFor, totalPlaysAgainst }
Or, if you want a more reusable solution, you can use parameter destructuring:
const getTeam = (
{ miscellaneous: { offensePlays: totalPlaysFor } },
{ miscellaneous: { offensePlays: totalPlaysAgainst } }
) => ({
totalPlaysFor,
totalPlaysAgainst
})
Then you can use it like:
const [ home ] = res.data.stats.home.teamStats
const [ away ] = res.data.stats.away.teamStats
const homeTeam = getTeam(home, away)

What is the most comfortable/conventional way to modify a specific item in an array, in a React component state?

I often find my self struggling with manipulating a specific item in an array, in a React component state. For example:
state={
menus:[
{
id:1,
title: 'something',
'subtitle': 'another something',
switchOn: false
},
{
id:2,
title: 'something else',
'subtitle': 'another something else',
switchOn: false
},
]
}
This array is filled with objects, that have various properties. One of those properties is of course a unique ID. This is what i have done recentely to edit a "switchOn" property on an item, according to its ID:
handleSwitchChange = (id) => {
const newMenusArray = this.state.menus.map((menu) => {
if (menu.id === id) {
return {
...menu,
switchOn: !menu.switchOn
};
} else {
return menu;
};
})
this.setState(()=>{
return{
menus: newMenusArray
}
})
}
As you can see, alot of trouble, just to change one value. In AngularJS(1), i would just use the fact that objects are passed by reference, and would directly mutate it, without any ES6 hustle.
Is it possible i'm missing something, and there is a much more straightforward approach for dealing with this? Any example would be greatly appreciated.
A good way is to make yourself a indexed map. Like you might know it from databases, they do not iterate over all entries, but are using indexes. Indexes are just a way of saying ID A points to Object Where ID is A
So what I am doing is, building a indexed map with e.g. a reducer
const map = data.reduce((map, item) => {
map[item.id] = item;
return map;
}, {})
now you can access your item by ID simply by saying
map[myId]
If you want to change it, you can use than object assign, or the ... syntax
return {
// copy all map if you want it to be immutable
...map
// override your object
[id]: {
// copy it first
...map[id],
// override what you want
switchOn: !map[id].switchOn
}
}
As an helper library, I could suggest you use Immutable.js, where you just change the value as it were a reference
I usually use findIndex
handleSwitchChange = (id) => {
var index = this.state.menu.findIndex((item) => {
return item.id === id;
});
if (index === -1) {
return;
}
let newMenu = this.state.menu.slice();
newMenu[index].switchOn = !this.state.menu[index].switchOn;
this.setState({
menu: newMenu
});
}

Categories

Resources