Why doesn't this change my redux state? - javascript

I have an array of array of objects in my state.
What I want to do is find the question with the correct id, then find the answer with the correct id to change it's value and update it to the state.
Here is what I got:
function updateObject(oldObject, newValues) {
return Object.assign({}, oldObject, newValues);
}
function updateItemInArray(array, questionId,answerId, updateItemCallback) {
const getQuestion = array.map(item => {
if(item.id !== questionId) {
return item;
}
})
const updatedItem = getQuestion[0].answers.map(answer => {
if(answer.id !== answerId) {
return answer;
}
​
const updatedItem = updateItemCallback(answer);
return updatedItem;
});
​
return updatedItems;
}
export function answerUpdate(state = [], action){
switch(action.type){
case 'ANSWER_UPDATE_FETCH_SUCCESS': {
const newAnswer = updateItemInArray(state.project, action.questionId, action.answerId, answer => {
return updateObject(answer, {value : action.newValue});
});
}
}
}
the object I'm looking through is kinda obvious but it looks something like this
project = [
question = {
id:"some Id",
answers: [
{
id:"another id",
value="someValue"
}
]
}
]
and some other properties but it is unrelevant for this question.
Thankful for every answer!

You need to update data in map itself instead of creating variable, map function returns new array with updated value and you are updating 0th index of array which won't be one you're looking for.
function updateItemInArray(array, questionId,answerId, newValue) {
return array.map(item => {
if(item.id !== questionId) {
return item;
} else {
item.answers.map(answer => {
if(answer.id !== answerId) {
return answer;
} else {
updateObject(answer, { value : newValue})
}
});
}
});
}
export function answerUpdate(state = [], action){
switch(action.type){
case 'ANSWER_UPDATE_FETCH_SUCCESS': {
return updateItemInArray(state, action.questionId, action.answerId, action.newValue);
}
}
}

Related

I am trying to make a function which will return an id from array of object

I want to write a function which will return id from array of object but when i call that function it returns me what I pass.
export function getRecipeByID(requestId) {
recipes.find(function (recipe) {
return recipe.id === requestId;
});
return requestId;
}
for example I call function
getRecipeByID(1)
and it returns 1.
I guess what you want to write is this:
export function getRecipeByID(requestId) {
return recipes.find(function (recipe) {
return recipe.id === requestId;
});
}
Notice that it doesn't return requestId but the result of recipes.find()
That's because you return requestId in your method while what you want is to return the result of recipes.find...
export function getRecipeByID(requestId) {
return recipes.find(function (recipe) {
return recipe.id === requestId;
});
}
you return requestId after using find which cause issue
const recipes = [
{
id: 2,
name: 'pasta'
},
{
id: 3,
name: 'sandwich'
},
{
id: 4,
name: 'pizza'
}
]
function getRecipeById(requestId) {
const findRecipe = recipes.find(function (recipe) {
return recipe.id === requestId;
});
return findRecipe;
}
console.log(getRecipeById(2)); // it will return{ id:2, name:"pasta" }
Try this:
export function getRecipeByID(requestId) {
const recipe = recipes.find(item => item.id === requestId);
if (recipe && recipe.id) {
return recipe.id;
} else {
return null;
}
}
Explanation: You're not assigning the result of the find operation (the element that has been found or null if there was no matching element) to any variable and are simply returning the request id.
Also: I'd suggest you'd look into arrow functions. They have been available for some years now and make your code much easier to read. :)
If you really want a function that return the same Id that you fetch, then do it:
export function getRecipeByID(requestId) {
return requestId;
}
otherwise, if want to fetch an Object of your list of object then you can simply try:
const array1 = [{}]
export function getRecipeByID(requestId) {
return array1.find(element => element.id == requestId);
}

Double for loop without mutating prop, VUE3

I have a 'data' props which say looks like this:
data = [
{
"label":"gender",
"options":[
{"text":"m","value":0},
{"text":"f","value":1},
{"text":"x", "value":null}
]
},
{
"label":"age",
"options":[
{"text":"<30", "value":0},
{"text":"<50","value":1},
{"text":">50","value":3}
]
}
]
In a computed property I want to have a new array which looks exactly like the data prop, with the difference that - for the sake of example let's say - I want to multiply the value in the options array by 2. In plain js I did this before, like this:
data.forEach(item => {
item.options.forEach(option => {
if (option.value !== null && option.value !== 0) {
option.value *= 2;
}
})
});
Now I'm trying to do this in a computed property, with .map(), so it doesn't mutate my data props, but I cant figure out how.
computed: {
doubledValues() {
var array = this.data.map((item) => {
//...
item.options.map((option) => {
//... option.value * 2;
});
});
return array;
}
}
you can use map() method, like so:
computed: {
doubledValues() {
return this.data.map(item => ({...item, options: item.options.map(obj => {
return (obj.value != null) ? { ...obj, value: obj.value * 2 } : { ...obj }
})})
);
}
}
Just copy objects/arrays. It will be something like that
computed: {
doubledValues() {
return this.data.map((item) => {
const resultItem = {...item};
resultItem.options = item.options.map((option) => {
const copyOption = {...option};
if (copyOption.value !== null && copyOption.value !== 0) {
copyOption.value *= 2;
}
return copyOption;
});
return resultItem;
});
}
}

How to fix the problem with second layer condition in redux reducer?

I'm making an application that gives tasks to learning methods. One of the reducers should change the state of the task mark: pass true or false depending on the solution of the task. But this code doesn't change the state of reducer.
My code:
const initialStateMethods = {
array: methodsObject
};
const methods = (state = initialStateMethods, action) => {
switch (action.type) {
case "CHANGE_MARK":
return {
...state,
array: state.array.map(method => {
if (method.id === action.methodIndex) {
method.tasks.map(task => {
if (task.id === action.taskIndex) {
return { ...task, mark: action.mark };
} else {
return task;
}
});
}
return method;
})
};
default:
return state;
}
};
But the value of the method changes easily and it works.
Example:
const methods = (state = initialStateMethods, action) => {
switch (action.type) {
case "CHANGE_MARK":
return {
...state,
array: state.array.map(method => {
if (method.id === action.methodIndex) {
return { ...method, name: "newName" };
}
return method;
})
};
default:
return state;
}
};
So I assume that the problem is in the multilayer structure
A small piece of the original object:
export const methodsObject = [
{
name: "from()",
id: 0,
tasks: [
{
taskName: "Task №1",
id: 0,
mark: null
},
{
taskName: "Task №2",
id: 1,
mark: null
}
]
}
You are missing a return statement next to the call of your map loop:
...
case "CHANGE_MARK":
return {
...state,
array: state.array.map(method => {
if (method.id === action.methodIndex) {
return method.tasks.map(task => {
if (task.id === action.taskIndex) {
return { ...task, mark: action.mark };
} else {
return task;
}
});
}
return method;
})
};

Why is my original state mutated when filtering with react redux

im using redux in an react app. Why is this filtering func mutating the original state.products? I cant understand why
state.products = [
{
type: "one",
products: [
{ active: true },
{ active: false }
]
}
]
function mapStateToProps(state) {
const test = state.products.filter((item) => {
if(item.type === "one") {
return item.products = item.products.filter((item) => {
item.active
});
}
return item;
});
return {
machineSearchWeightRange: state.machineSearchWeightRange,
filteredItems: test //This will have only products active
};
}
filteredItems will have only products that is active but the state.products is also updated containing only active products when trying to filter on the same data again.
Suggestions
Because you're assigning to a property on an existing state item:
function mapStateToProps(state) {
const test = state.products.filter((item) => {
if(item.type === "one") {
return item.products = item.products.filter((item) => { // <========== Here
item.active
});
}
return item;
});
return {
machineSearchWeightRange: state.machineSearchWeightRange,
filteredItems: test //This will have only products active
};
}
Instead, create a new item to return. Also, it looks like you need map along with filter, and you're not actually returning item.active in your inner filter (see this question's answers for more there):
function mapStateToProps(state) {
const test = state.products.filter(({type}) => type === "one").map(item => {
return {
...item,
products: item.products.filter((item) => {
return item.active;
})
};
});
return {
machineSearchWeightRange: state.machineSearchWeightRange,
filteredItems: test //This will have only products active
};
}
Side note: This:
products: item.products.filter((item) => {
return item.active;
})
can be simply:
products: item.products.filter(({active}) => active)

In React/Redux reducer, how can I update a string in an nested array in an immutable way?

My store looks like this,
{
name: "john",
foo: {},
arr: [
{
id:101,
desc:'comment'
},
{
id:101,
desc:'comment2'
}
]
}
My textarea looks like this
<textarea
id={arr.id} //"101"
name={`tesc:`}
value={this.props.store.desc}
onChange={this.props.onChng}
/>
My action is
export const onChng = (desc) => ({
type: Constants.SET_DESC,
payload: {
desc
}
});
My reducer
case Constants.SET_DESC:
return update(state, {
store: {
streams: {
desc: { $set: action.payload.desc }
}
}
});
It works only if arry is an object, I had to make changes to the stream to an array and I am confused how I can update to an array, also how does get the right value from the store.
The following example taken from the redux documentation might help you in the use case how to update items in an array. For more on this you can read on here http://redux.js.org/docs/recipes/StructuringReducers.html
state structure is something like this
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
and reducer code is like below
function updateObject(oldObject, newValues) {
// Encapsulate the idea of passing a new object as the first parameter
// to Object.assign to ensure we correctly copy data instead of mutating
return Object.assign({}, oldObject, newValues);
}
function updateItemInArray(array, itemId, updateItemCallback) {
const updatedItems = array.map(item => {
if(item.id !== itemId) {
// Since we only want to update one item, preserve all others as they are now
return item;
}
// Use the provided callback to create an updated item
const updatedItem = updateItemCallback(item);
return updatedItem;
});
return updatedItems;
}
function appReducer(state = initialState, action) {
switch(action.type) {
case 'EDIT_TODO' : {
const newTodos = updateItemInArray(state.todos, action.id, todo => {
return updateObject(todo, {text : action.text});
});
return updateObject(state, {todos : newTodos});
}
default : return state;
}
}
If you have to update an element in a array within your store you have to copy the array and clone the matching element to apply your changes.
So in the first step your action should contain either the already cloned (and changed) object or the id of the object and the properties to change.
Here is a rough example:
export class MyActions {
static readonly UPDATE_ITEM = 'My.Action.UPDATE_ITEM';
static updateItem(id: string, changedValues: any) {
return { type: MyActions.UPDATE_ITEM, payload: { id, changedValues } };
}
}
export const myReducer: Reducer<IAppState> = (state: IAppState = initialState, action: AnyAction): IAppState => {
switch (action.type) {
case MyActions.UPDATE_ITEM:
return { ...state, items: merge(state.items, action.payload) };
default:
return state;
}
}
const merge = (array, change) => {
// check if an item with the id already exists
const index = array.findIndex(item => item.id === change.id);
// copy the source array
array = [...array];
if(index >= 0) {
// clone and change the existing item
const existingItem = array[index];
array[index] = { ...existingItem, ...change.changedValues };
} else {
// add a new item to the array
array.push = { id: change.id, ...change.changedValues };
}
return array;
}
To update an array, I would use immutability helper and do something like this - to your reducer
let store = {"state" : {
"data": [{
"subset": [{
"id": 1
}, {
"id": 2
}]
}, {
"subset": [{
"id": 10
}, {
"id": 11
}, {
"id": 12
}]
}]
}}
case Constants.SET_DESC:
return update(store, {
"state" : {
"data": {
[action.indexToUpdate]: {
"subset": {
$set: action.payload.desc
}
}
}
}
})
});

Categories