Redux - How to add entry to array in reducer - reactjs

I stuck with this bit and I can't progress - I guess solution is simple but I can't figure out. I'm trying to add entry in reducer so data in in would look something this:
state = {
entryId: {
entryName: ["something", "something2", "something3" /* and so on... */]
}
};
So far this is the closest I get, but, instead of adding new unique entry, it is replacing the one that is stored already. Also I need to be able to add this item to empty state where entryId, entryName doesn't exist yet to avoid error:
switch(type) {
case ADD_ENTRY:
return {
...state,
[entryId]: {
...state[entryId],
[entryName]: {
[uniqueEntry]: true
}
}
};
}
Any idea what I'm doing wrong?

If you're trying to add an element to the end of the entryName array you should be doing:
return {
...state,
[entryId]: {
...state[entryId],
[entryName]: [
...state[entryId][entryName],
uniqueEntry
]
}
};
ES6 spread with arrays works like this:
const array1 = [1, 2, 3];
const array2 = [4, 5, 6];
const eight = 8;
const newArray = ['stuff', ...array1, 'things', ...array2, ...[7, eight], 9];
console.log(newArray); // ["stuff", 1, 2, 3, "things", 4, 5, 6, 7, 8, 9]
Check out this gist which has an example of something very similar to what you're doing.
I found this set of examples extremely helpful as well. A lot of great stuff in here:
https://github.com/sebmarkbage/ecmascript-rest-spread
Update:
If entryName is initialized to undefined like you say in your comment, you could do this:
return {
...state,
[entryId]: {
...state[entryId],
[entryName]: [
...state[entryId][entryName] || [],
uniqueEntry
]
}
};
I think this is a pretty great example of how painful it can be working with React/redux using a heavily nested data structure. FWIW, it's been recommended to me many times to flatten your state as much as possible.

Related

Array return are sort in reverse and i dont know why

I have a weird problem that I can't solve since yesterday on Garry's Mod (GLua)
When my gmod server game is running, I notice that there are errors on arrays that are empty with certain keys when they are well specified, while doing deep tests, I noticed that the returned arrays were ... backwards.
Here is an array below:
bigArray = {
[ "default" ] = { 4, 2, 1 },
[ "police" ] = { 4, 2, 1 },
[ "mayor" ] = { 5, 2, 1 },
[ "sherif" ] = { 6, 2, 1 },
}
Good, next I will use the PrintTable() method (PrintTable() is a method already integrated in the game) which displays the contents of an array (This method, normally if I run PrintTable(bigArray) the result should be literally the array above, but here is the result displayed:
{
[ "sherif" ] = { 6, 2, 1 },
[ "mayor" ] = { 5, 2, 1 },
[ "police" ] = { 4, 2, 1 },
[ "default" ] = { 4, 2, 1 },
}
I will put an example more telling since the previous one is an dictionary and not really an array :
table = {
'truc',
'machin',
'chose'
}
If I display the first element of the table like this print(table[1]), the displayed result will be: chose
Flipping the tables upside down makes a lot of addons I use crash, I have no idea how this happened, it happened suddenly without me modifying any addon (I already looked at the worshop addons, none of them are responsible for the problem)
If someone has an idea how this could be caused, I'm interested, thanks.
Edit :
I installed my project on a virtual machine, and when I launched the server, I had none of the errors I mentioned.
I formatted my entire machine containing the errors and since then the problem is solved.
Following this observation, I still think that the problem did not come from the code, maybe my assembler or the game was corrupted, who knows.
Thanks for those who tried to answer my problem, and if someone one day encounters the same problem, I strongly advise him to check the integrity of their game.
I noticed that the returned arrays were ... backwards
They are not arrays. They are dictionaries (unordered set of key-value pairs).
An array in Lua would look like the following:
bigArray = {
{ name="default", 4, 2, 1 },
{ name="police", 4, 2, 1 },
{ name="mayor", 5, 2, 1 },
{ name="sherif", 6, 2, 1 },
}
In this case order of elements is preserved:
$ lua
Lua 5.4.4 Copyright (C) 1994-2022 Lua.org, PUC-Rio
> bigArray = {
{ name="default", 4, 2, 1 },
{ name="police", 4, 2, 1 },
{ name="mayor", 5, 2, 1 },
{ name="sherif", 6, 2, 1 },
}
> bigArray[1].name # This will _always_ be "default"
default
In Garry's Mod dictionaries are not stored in any particular order. If you want to iterate through a dictionary in order, rather than using pairs you must use either SortedPairs, SortedPairsByMemberValue or SortedPairsByValue.
See this wiki page for reference.
For your implementation, I would recommend adding a field to each member value of your bigArray dictionary to specify a sort order; for instance:
local bigArray = {
[ "default" ] = {
sortOrder = 1,
myValues = { 4, 2, 1 }
},
[ "police" ] = {
sortOrder = 2,
myValues = { 4, 2, 1 }
},
[ "mayor" ] = {
sortOrder = 3,
myValues = { 5, 2, 1 }
},
[ "sherif" ] = {
sortOrder = 4,
myValues = { 6, 2, 1 }
}
}
This would then allow you to iterate in order of the sortOrder value, like so:
for key, value in SortedPairsByMemberValue(bigArray, "sortOrder") do
print("\n" .. key .. ":")
PrintTable(value)
end

How do I add new values to state using Typescript?

you need to add new values to the array, I can't understand what the problem is.
When you click on a checkbox, you need to get the id of this checkbox and write it to the array of answers for the corresponding question
type Result = number;
interface Answer {
result: Result[];
}
const answers: Answer[] = [];
questions.forEach(() => {
answers.push({
result: [],
});
});
const [currentAnswer, setNewAnswer] = useState<Answer[]>(answers)
const handleChange = (e:React.ChangeEvent<HTMLInputElement>) =>{
// console.log(typeof(currentAnswer),currentAnswer);
if(e.target.checked){
console.log(currentAnswer[currentQuestion].result.push(Number(e.target.id)));
setNewAnswer(
currentAnswer[currentQuestion].result.push(Number(e.target.id) // ERROR HERE
)
...
I got error
const currentAnswer: Answer[]
// Argument of type 'number' is not assignable to parameter of type 'SetStateAction<Answer[]>'
should use .concat() in this situation to return new array
.push() will only return new length which is number and incompatible with the type you make.
setNewAnswer(
currentAnswer[currentQuestion].result.concat(Number(e.target.id)) // ERROR HERE
)
To expand on Mic Fung's answer, push mutates the existing array and doesn't return the new array.
const myArray = [1, 2, 3]
myArray.push(4) // returns 4, which is the new array length
console.log(myArray) // [1, 2, 3, 4]
concat doesn't mutate the existing array, but instead returns a new array
const myArray = [1, 2, 3]
const myNewArray = myArray.concat(4)
console.log(myNewArray) // [1, 2, 3, 4]
console.log(myArray) // [1, 2, 3]
When working with React, you should avoid directly mutating the state. Instead, create new values and pass them to the setState function. This is why functions like concat are preferred over ones like push, as they avoid the mutation.

Extract array inside Objects

I am using ReactJS.
I have a JSON:
{
"randomNum1": [
1,
2,
3,
4
],
"randomNum2": [
5,
6,
7,
8
]
}
What I wanted to do is to get the array of randomNum1 and randomNum2. This is what I did:
for(let i = 0; i<data.randomNum1.length; i++)
and I get this error:
Cannot read property 'length' of undefined
The reason why I did that is because when I do a console.log(data.randomNum1) I am able to see the array: [array][1]
Is it because it's still an Object which is why .length is not allowed? If so, how can I get the values of those numbers and store it in an array?
[1]: https://i.stack.imgur.com/nLbdA.png
let allValues=[]
let the_json_object={
"randomNum1": [
1,
2,
3,
4
],
"randomNum2": [
5,
6,
7,
8
]
}
const keys = Object.keys(the_json_object);
const values = Object.values(the_json_object);
let Key_len = keys.length;
for(i=0;i<Key_len;i++){
let len = values[i].length;
for(j=0;j<len;j++)
{
if(values[i][j])
{
allValues.push(values[i][j])
}
}
}
//allValues contains all the values from all arrays.
Your data object is probably undefined at the moment you are entering the loop.
The reason you see it on console.log is because objects are passed by reference. When you console.log the object and then click to see his properties you get the updated object. If you'll do console.log(JSON.stringify(data)) you will see the object is probably empty.
Please provide more code in order to understand the issue here.

Simple reducer accumulator should not be mutating the state, why is it?

For some reason my reducer is only returning a single element in the categories collection.
I'm just learning this accumlator logic, so I have kept it simple which I thought was suppose to simply return the exact same state as I started with.
My state in JSON looks like:
{
"account":{
"id":7,
"categories":[
{
"id":7,
"products":[
{
"productId":54
}
]
},
{
"id":9,
"products":[
{
"productId":89
}
]
}
]
}
}
In my reducer I have the following:
return {
...state,
account: {
...state.account,
categories: [state.account.categories.reduce((acc, cat) => {
return {...acc, ...cat}
}, {})]
}
};
When I output my state, I see that for some reason it has removed one of the categories from the collection.
Why isn't my state the same since the accumulator isn't filtering anything? It should be the exact same as it started with.
if you want to return categories unchanged (but with a different reference), you should do it like this:
categories: state.account.categories.reduce((acc, cat) => {
return [...acc, cat];
}, [])
In your code accumulator value is an object with categories props that is constantly overwritten by another item from an array so in the end only the last item is present.
Let's put aside react and redux and focus on reduce function. It takes an array and returns something different using the function called a reducer.
In the following example:
const array = [{num: 1}, {num: 2}];
you can take each of the elements of an array and merge their properties:
array.reduce((acc, item) => ({...acc, ...item}), {})
this is equal to
Object.assign({}, array[0], array[1]);
or
{...array[0], ...array[1]}
and result is {num: 2}, (first there was an empty object {}, then {num: 1} and finally {...{num: 1}, ...{num: 2}} gave {num: 2}
It doesn't matter if you enclose it in an array, it's still a one object created from merging all objects in the array together
If you want a copy of an array. This can be done like this:
array.reduce((acc, item) => [...acc, item], []);
This is equal to
[].concat(...array);

Correct way to update a state array in a Flux Store

I have a Flux Store and a state array that needs updating when a new comment is created so that a list of comments updates in the view. I just want to confirm that I am updating it correctly using push:
this.state.comments.push(commentArray);
It works fine but I have read about immutability but as this is a store and not a view I take it this is ok?
onDispatcherAction: function (payload) {
var action = payload.action;
if (ActionTypes.CREATE_CONFIGURATION_SETTINGS_RESPONSE === action.type) {
this.handleResponseErrors(action.data);
var commentArray = {
_id: action.data._id,
user: {
id: action.data.user.id
},
id:action.data.id,
commentname: action.data.commentname,
timeCreated: action.data.timeCreated
}
this.state.commentname = action.data.commentname;
this.state.comments.push(commentArray);
this.emitChange();
}
}
You probably should take a look to the Immutability Helpers.
From React Documentation
Simple push
var initialArray = [1, 2, 3];
var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]
initialArray is still [1, 2, 3].

Resources