Reactjs array mutation - javascript

I have this function:
setNotActiveWalletsList = () => {
const { GetAccounts } = this.props;
let shallowCopyOfWalletsArray = [...GetAccounts]
const notActive = shallowCopyOfWalletsArray.filter(user => user.active !== true);
let newArr = notActive.map(item => {
return decryptAccountInformation(item).then(result => {
!result.address ? null : item.address = result.address
})
});
this.setState({ onlyNotActive: newArr });
}
GetAccounts is an array of objects
The issue is, One of my colleagues have told me that I am mutating the array with this line:
!result.address ? null : item.address = result.address
But I don't really understand why is this considered a mutation? I am sure I created a copy of the original array and modified it.
Any suggestions on how to resolve this, please?

Spread syntax just does a one level closing of the object or array. Any object or array which is more than one level deep will still have the same reference. Hence when you using notActive array items, you are essentially working on the same reference that was inside GetAccounts
The correct way to update is to return the cloned and updated reference from within the map function and using Promise.all to also handle the async call
setNotActiveWalletsList = () => {
const { GetAccounts } = this.props;
let shallowCopyOfWalletsArray = [...GetAccounts]
const notActive = shallowCopyOfWalletsArray.filter(user => user.active !== true);
let promises = notActive.map(item => {
return decryptAccountInformation(item).then(result => {
return !result.address ? item : {...item, address: result.address}
})
});
Promise.all(promises).then(newArr => this.setState({ onlyNotActive: newArr }));
}

Related

Items not getting pushed to an array inside the Javascript promise

In the following exported function which is from a Nextjs app as an API page, the domainnames array is returning nothing in the 200 response.
However, if I do not use the GetDomainStatus() function and just push items from response.data.results into domainnames, then the JSON response is filled.
export default function GetSuggestions(req, res){
const keyword = req.query.q;
const tlds = '.com,.net,.io,.org,.co,.xyz,.app,.us,.blog,.shop,.land,.video,.review,.host,.dev';
let queryPath = `${suggestionsURL}?include-registered=false&tlds=${tlds}&include-suggestion-type=true&sensitive-content-filter=true&use-numbers=true&max-length=20&lang=eng&max-results=100&name=${keyword}&use-idns=false`
let domainnames = [];
axios.get(queryPath).then(response => {
response.data.results.forEach(item => {
GetDomainStatus(item.name).then(a => {
domainnames.push({
name: item.name,
avail: a
})
})
})
res.status(200).json(domainnames);
});
}
is this a scope issue where I actually cannot access domainnames array from within the promise?
This solution worked. Not sure if its the best, but uses the promise.all solution.
const domainnames = [];
const promises = [];
axios.get(queryPath).then(response => {
response.data.results.forEach(item => {
let newPromise = GetDomainStatus(item.name).then(a => {
domainnames.push({
name: item.name,
avail: a
})
});
promises.push(newPromise);
})
Promise.all(promises).then(r => res.status(200).json(domainnames));
});

Can't iterate through array from an async Javascript function?

I'm stuck on an issue where I'm parsing the results from an API in getSubscriptions(). This calls getUserSubs() which returns the following object:
When I call on subscriptions I get the expected array console (See "Works") snippet.
But when I try to iterate on the array subscriptions (See "Doesn't work"), then the contents of the function are not even called.
userSubs() is called for API data
async function getUserSubs(userId) {
const ref = collection(db, "users", userId, "subscriptions")
let subList = []
try {
const subIds = await getDocs(ref)
subIds.forEach(subRef => {
const docRef = subRef.data()
getDocData(docRef.product).then(product => {
subList.push(product)
})
})
return subList
} catch (e) {
console.error("Error in getting subscriptions: ", e)
return []
}
}
Works
function getSubscriptions(userId) {
getUserSubs(userId).then(subscriptions => {
console.log(subscriptions) // Works as intended
}
}
Doesn't work
function getSubscriptions(userId) {
getUserSubs(userId).then(subscriptions => {
subscriptions.forEach(x => {
console.log(x) // ISSUE: This isn't called
})
}
Also doesn't work
let a = []
getUserSubs(userId).then(subscriptions => {
subscriptions.forEach(x => a.push(x))
})
console.log(a)
I know there are similar questions asked but after reading them I'm still not able to resolve my issue.
Similar issues:
How to access the value of a promise?
Using async/await with a forEach loop
getUserSubs(userId).then(subscriptions => {
console.log(subscriptions) // Works as intended
}
No it doesn't. It only appears so because you are inspecting the live array that was mutated after it has been logged to the console.
Also doesn't work:
let a = []
getUserSubs(userId).then(subscriptions => {
subscriptions.forEach(x => a.push(x))
})
console.log(a)
Yes, for rather obvious reasons: the array is logged before you fill it. It would need to be either
getUserSubs(userId).then(subscriptions => {
let a = []
subscriptions.forEach(x => a.push(x))
console.log(a)
})
or
let a = []
const subscriptions = await getUserSubs(userId)
subscriptions.forEach(x => a.push(x))
console.log(a)
But none of these will solve your core problem: getUserSubs returns an empty array before it gets filled, in the lines
subIds.forEach(subRef => {
const docRef = subRef.data()
getDocData(docRef.product).then(product => {
subList.push(product)
})
})
return subList
you never wait for the getDocData promise. If you change that to
let subList = []
for (const subRef of subIds) {
const docRef = subRef.data()
const product = await getDocData(docRef.product)
subList.push(product)
}
return subList
or just
return Promise.all(subIds.map(subRef => {
const docRef = subRef.data()
return getDocData(docRef.product)
}))
it would work, as described in the question you already found.
(This might still not work. subIds looks suspiciously like a firebase snapshot, which is not an array and can neither be iterated nor does it have a .map method. In that case, you'll need to use forEach+push manually).
Honestly I wouldn't use the forEach() method. I think all you need to do to fix this is iterate over the results in a normal for loop.
for(let subscription of subscriptions) {
console.log(subscription);
}
OR
for(let index in subscriptions) {
console.log(subscriptions[index]);
}
If this doesn't do the trick, I'll open up a sandbox and look more in depth.

What to put in the dependency array in React.useMemo

Consider situation:
const App = ({ data = [], users = [] }) => {
const selectedId = React.memo(() => {
return data.find((id) => id === 'someId');
}, [data]);
const userIds = React.memo(() => {
return users.map(({ id }) => id);
}, [users]);
const finalIds = React.memo(() => {
return userIds.find((id) => id === selectedId);
}, []);
return finalIds.toString();
}
What should I put in the dependency array in finalIds?
Should I:
provide the props used by variables that are used here, e.g. - [data, users]
provide the variables that are used here, e.g. - [selectedId, userIds]
keep it empty
You put any variables that you use inside the calculation. In other words, [selectedId, userIds]. When those variables change, you need to repeat the calculation, or finalIds will be stale.
If you use eslint, i'd recommend using eslint-plugin-react-hooks. It can spot if you're missing a variable from the dependency array and fill them in automatically.

Filter through an array and return Params

I am trying to filter through an array of data from my API, however, the data returns unfiltered, I am thinking its something to do with the way I structured my method or It could be that my params are not being returned. I have below both my codes. I would really appreciate any effort. Thanks
Trying to filter data
const [catalogueArray, setCatalogueArray] = useState([])
useEffect(() => {
catalogueList()
}, [])
const catalogueList = () => {
const catalogue = data.filter(item => {
item.storeName == navigation.state.params.enteredStore
return { ...item }
})
setCatalogueArray(catalogue)
}
Setting Params
const handleSelectedStore = (name) => {
setSelectStore({ selectStore: name })
navigation.navigate('StoreCatalogue', { selectStore: name })
}
You are returning an object, not a boolean in filter(). Objects are truthy so you are effectively doing no filtering at all and are getting the same result as if you did return true
The == comparison above the return is doing nothing since you aren't using the result of that comparison anywhere
The equality check is what you want to return. If you want new objects in the array you need a map() for that in another step
const catalogue = data.filter(item => {
return item.storeName == navigation.state.params.enteredStore
}).map(item => ({ ...item }));

Reference error, says variable is not defined

So I have a function which reads from Firebase database and then creates an array of objects. I have defined variable key, but it is still unavailable.
var usersList = [];
const ref = firebase.database().ref()
function fetchUsers() {
ref.child('users').once('value').then(snap => {
var promises = [];
snap.forEach(childSnap => {
var key = childSnap.key
promises.push
(ref.child(`users/${key}/points`).once('value')
);
});
return Promise.all(promises);
}).then(function(snapshots) {
return snapshots.map(snapper => {
var points = snapper.val()
return {uid: key, points: points};
})
}).then(function(usersList) {
console.log(usersList)
})
}
And this is the error I get...
(node:11724) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): ReferenceError: key is not defined
If i just do: key = childSnap.key, then every object's uid is the same.
key is only defined within the forEach callback. Naturally you cannot then reference it from within the second then callback. Moreover, which key would it be? For the first entry? The second? Third?
Instead, you need to return the key with the value:
function fetchUsers() {
ref.child('users').once('value').then(snap => {
var promises = [];
snap.forEach(childSnap => {
var key = childSnap.key;
promises.push(
ref.child(`users/${key}/points`).once('value').then(
snapper => ({key, snapper}) // **
)
);
});
return Promise.all(promises);
}).then(function(snapshots) {
return snapshots.map(({key, snapper}) => { // **
var points = snapper.val();
return {uid: key, points: points};
});
}).then(function(usersList) {
console.log(usersList);
});
}
On the first ** line above, we're transforming the result from once so it returns an object with both the key and "snapper".
On the second ** line, we're using destructured parameters to receive those objects as the distinct key and snapper parameters.
FWIW, if you want to aggressively use concise arrows:
function fetchUsers() {
ref.child('users').once('value').then(snap =>
Promise.all(snap.map(childSnap => {
const key = childSnap.key;
return ref.child(`users/${key}/points`)
.once('value')
.then(snapper => ({key, snapper}));
}))
).then(snapshots =>
snapshots.map(({key, snapper}) => ({uid: key, points: snapper.val()}))
).then(usersList => {
console.log(usersList);
});
}
Also note the use of map rather than declaring an array and pushing to it from a forEach callback. :-)

Categories

Resources