React/Redux update state array if action id is not already present - reactjs

I have a set of actions to update Redux state. I'm struggling filtering the end results. Here are my actions
GET_ITEMS (GET request, returns a list)
SET_ITEM (POST request, payload: 'id')
DELETE_ITEM (DELETE request, payload: 'id')
Here are my examples:
case 'GET_ITEMS':
return {
...state,
list: action.payload, // [{ id: 'one', count: 1 }, { id: 'two', count: 2}]
};
case 'SET_ITEM':
return {
...state,
list: [...state.list, { id: action.payload, count: state.list.length }],
};
case 'DELETE_ITEM':
return {
...state,
list: state.list.filter(item => item.id !== action.payload),
};
Only the GET_ITEMS action type returns a response data, the rest I have to manually update them. Bellow are my questions in the same order
How to update the state list array, compared to response data list array?
How to add an object item based on a id action payload?
Delete seems to be working fine

You can use a helper function for your SET_ITEM action:
case 'SET_ITEM':
return {
...state,
list: updateList(state.list, action)
};
function updateList(list, action) {
const found = list.find((element) => element.id === action.payload);
if (found) {
// ...update found props
return list;
}
return [...list, { id: action.payload, count: list.length + 1 }];
}
I would definitely move the count property outside of the element because the way it's modeled we'll have unnecessary count property for every element and if we remove an element in the middle of the array, the count property will be wrong for every element on the right side. So a better state would be:
{
...state,
list,
listCount
}

Related

Having problem in understanding REDUX code

I am trying to create shopping basket through Redux Toolkit. I am finding it hard to understand this piece of code that what is purpose of all this code. Specifically those if conditions. Cant understand how add and remove reducer is working
const basketSlice = createSlice({
name: "basket",
initialState: INITIAL_STATE,
reducers: {
add: (state, action) => {
return state.map(item => {
if (item.id !== action.payload.id) {
return item
}
return {
...item,
added: true
}
})
},
remove: (state, action) => {
return state.map(item => {
if (item.id !== action.payload.id) {
return item
}
return {
...item,
added: false
}
})
}
}
})
You should check https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Basically it loops over the state items, creating a new array from what is returned in each iteration of the loop.
So, what it does for, say, the remove reducer:
Loop over each item in state, each time returning something that will be an entry in the new array
The if section checks if the id of the current loop element is the same than the one we want to remove: if it's not the same ID, we return the item "as is", if it's the same ID, we return added: false so we know it was removed.
In the end, you get a new array that was processed through this map function, allowing to do whatever check you need to.
Say I have an array with 3 items:
const state = [
{ id: 12, name: "Fancy Phone", added: true, },
{ id: 54, name: "Leather Jacket", added: true, },
{ id: 564, name: "AI World-Dominating Robot", added: true, },
]
And I want to remove the "AI World-Dominating Robot" because I don't want anymore trouble:
// Create a new array from the .map
return state.map(item => {
// here we loop over each item one by one
// IF the ID in the action payload (thus the ID you want to remove) is not the same as the current item ID, we don't want to remove it
if (action.payload.id !== item.id) {
return item // so we return the item "as-is", and as we returned something, the .map loop moves to the next item
}
return { ...item, added: false } // otherwise, we set "added: false" to flag the fact it's removed

Replacing object for another in state React

I have an array of objects. I find the object I need to replace but can't replace it in state. The original object stays untouched and the object I want to replace it with is added to the end of the array. I'm use React hooks. Thanks
case types.ADD_USERNAME:
let task = action.payload
let assignedTask = state.tasks.find(el => el.id === task.id)
let index = state.tasks.indexOf(assignedTask)
return {
...state,
tasks: [
...state.tasks,
assignedTask = {
...assignedTask,
user: task.user.name
}
]
}
You can make use of map instead of going by index and find element to update the state which in my opinion is a cleaner approach
case types.ADD_USERNAME:
let task = action.payload;
return {
...state,
tasks: state.tasks.map(el => {
if(el.id == task.id) {
return { ...el, user: task.user.name}
}
return el;
});
}
However if you wish to go by the index and find element approach, you need to slice the task array and update the specific index element
case types.ADD_USERNAME:
let task = action.payload
let index= state.tasks.findIndex(el => el.id === task.id);
return {
...state,
tasks: [
...state.tasks.slice(0, index - 1),
{
...state.tasks[index],
user: task.user.name
}
...state.tasks.slice(index + 1)
]
}
You could achieve this by using map and matching your object by id or any other unique identifier:
case types.ADD_USERNAME:
let task = action.payload;
return {
...state,
tasks: state.tasks.map(t => {
if (t.id === task.id) {
return {
...t,
user: task.user.name
};
}
return t;
})
};

Delete an element from array using redux

I am trying to make a todo app using redux and I'm stack on how to delete a todo from the array.
reducer.js
export default function todo(state, action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
id: action.id,
text: action.text,
completed: false
}
case 'REMOVE_TODO':
return {
id: action.id,
...state.slice(id, 1)
}
default:
return state;
}
}
action.js
let nextTodoId = 0
export const addTodo = text => ({
type: 'ADD_TODO',
id: nextTodoId++,
text
})
export const removeTodo = id => {
type: 'REMOVE_TODO',
id
}
So far i can add and toggle a todo as completed or not. Thanks
Using redux you need to return all array elements except the removed one from reducer.
Personally, I prefer using the filter method of the Array. It'll return you a shallow copy of state array that matches particular condition.
case 'REMOVE_TODO':
return state.filter(({id}) => id !== action.id);
In react redux application, you should know, you always have to create a new object,
to deleting an item please use spread operator like this :
return [...state.filter(a=>a.id !== id)]

Updating state with nested array of objects

This is something also called as deep state update. Where I have to update nested state array.
I am implementing a simple redux application. Here I want to update the state which is nested array of object. My reducer function takes state, action. I have to update responses property of state with new value. I tried to map/iterate the state but it isnt working for me. Is there a way to update those specific values and return update state.
const sampleData = [{
"id": 1,
"text": "Hobby",
"answers": [{
"id": 1,
"text": "Chess",
"responses": 5
}]
}];
const action = {
type: "VOTE",
questionId: 1,
answerId: 3
};
This is handleSubmit function I am calling when Submit button is clicked on form.
handleSubmit(e){
const store = createStore(hobbyReducer, hobby); // created store here
var k = (document.querySelector('input[name = "hobby"]:checked').value);
store.dispatch({type:"SUBMIT", text: k, id: 1}); // Dispatching action to reducer
store.subscribe(()=>{
console.log(store.getState());
});
}
Here is reducer part of program:
function hobbyReducer(state, action) {
switch(action.type){
case "SUBMIT":
return{
...state,
answers: state.answers.map(e=> (e.text === action.text && e.answers.id === action.id)?
{ ...answers,
responses: (e.responses+1)} :
hobby )
}
break;
default:
return state;
}
}
initial state = sampleData; // Array object given above
I am unable to update the responses property which is in a nested array
This is the code I wanted to write, after some research I finally did what was required. Although this is not solution in terms of time complexity.
`
case "SUBMIT":
const updatedState = state.map(e =>{
if(e.id === action.id)
return{...e,
answers: e.answers.map(f =>{
if(f.text === action.text){
return{
...f,
...{responses: f.responses + 1},
}
}
return f;
})
}
return e;
})
console.log("updatedstate", updatedState)
return updatedState
Just an error in your map I think:
function hobbyReducer(state, action) {
switch(action.type) {
case "SUBMIT":
return {
...state,
answers: state.answers.map(answer => {
if (answer.text === action.text && answer.id === action.id) {
answer.response = answer.response + 1;
}
return answer;
});
}
default:
return state;
}
}

Redux's Reducer - updating with keys as numbers

In my current implementation, I have an initial state as so:
const state = {
1: {id: 1, status: false},
2: {id: 2, status: false}
}
I'm having a hard time how to implement the reducer correctly if I wanted to update the status on either the item.
I have the following as so:
export default function (state = initialState, action) {
switch (action.type) {
case UPDATE_TO_TRUE: {
const { itemID } = action;
let item = itemID.toString();
return {
...state,
item: {
status: true
}
}
}
default: {
return state;
}
}
}
Essentially I can't figure out how to pinpoint the right key. What's tricky is that the keys are setup as numbers.
Try using a computed property key in the object literal definition:
return {
...state,
[item]: {
id: item,
status: true
}
}
You can access the item you want using bracket notation, which works as long as the key can be represented by a string. So for example:
const state = {
1: {id: 1, status: false},
2: {id: 2, status: false}
};
console.log(state['2']);
returns your second item.

Resources