React-Native Redux Create Array in Array - javascript

The main problem is about react-native redux/javascript in general.
I want to create a special kind of "Todo-list", which means, that there are parent categories (in this case PackingLists) and each of them includes a name, two dates and the item array.
That means:
We assume, that we have a list of "Packinglists" (e.g. Madrid, Paris and Berlin).
It should be possible to add items to each of them (e.g. Madrid contains a Jeans and Shirt and Paris a skirt & a pair of shoes). The idea behind the result should be clear now!
There is no problem in the creation of the Packinglists. I just donĀ“t know how to "append"/modify the "Packinglist" and add/remove items from the array in each list with react-redux (in PackingListReducer.js - the problem is in add_ItemToPackingList) . Can you help me?
index.js (actions)
export const addPackingList = (name, startDate, endDate, items) => ({
type: 'add_PackingList',
payload: {
name,
startDate,
endDate,
items
}
});
export const addItemToPackingList = (parentItem, itemName, isDone) => ({
type: 'add_ItemToPackingList',
payload: {
parentItem,
itemName,
isDone,
}
});
export const removePackingList = (name) => ({
type: 'remove_PackingList',
payload: name
});
PackingListReducer.js
export default (state = [], action) => {
switch (action.type) {
case 'add_PackingList':
return [...state, action.payload];
case 'add_ItemToPackingList': {
const currentList =
state.filter(packingList => packingList.name === action.payload.parentItem);
console.log(state);
console.log(currentList);
//return { ...state, items: [...currentList, action.payload] };
return Object.assign(state, { items: [...currentList, action.payload] });
}
case 'remove_PackingList':
return state.filter(packinglist => packinglist.name !== action.payload);
default:
return state;
}
};
Here I create the parent "PackingList", which works fine.
<TouchableOpacity
onPress={() => {
const exists = this.props.packingList.includes(
this.props.packingList.find(item => item.name === this.state.text));
console.log(endDate);
if (!exists) {
this.props.addPackingList(this.state.text, startDate, endDate, []);
this.props.navigation.goBack();
}
}
}
>
<Text style={styles.buttonStyle}>
CREATE NEW LIST!
</Text>
</TouchableOpacity>
</View>
The creation of the "item", which should be added to the parent packing list.
<TextInput
style={styles.inputText}
onSubmitEditing={() => {
this.props.addItemToPackingList(this.props.route.params.name,
this.state.text, false);
}}
</TextInput>

Try this:
Edited
// PackingListReducer.js
const currentListIndex =
state.findIndex(packingList => packingList.name === action.payload.parentItem);
let currentList = state[currentListIndex];
currentList = { ...currentList, items: [...currentList.items, action.payload] };
const newState = [...state];
newState[currentListIndex] = currentList;
...
return newState;
Updated
To delete the same items, reducer case would look like this (given the fact that you send itemName to your reducer in a payload):
case 'remove_ItemFromPackingList': {
const currentListIndex =
state.findIndex(packingList => packingList.name === action.payload.parentItem);
let currentList = state[currentListIndex];
currentList = { ...currentList, items: currentList.items.filter(item => item.itemName !== action.payload.itemName) };
const newState = [...state];
newState[currentListIndex] = currentList;
return newState;
}

Related

How to prevent react context from adding duplicate item to cart

I am using react context to add item to the cart and I want to prevent duplicate items being added in the cart, I have tried tweaking the code, but it seems like something is missed. Below is the code sample:
const cartReducer = (cartState, action) => {
switch (action.type) {
case "add-cart": {
const item = action.payload;
const existItem = cartState.cart.find((x) => x.id === item.id);
if (existItem) {
return {
cart: [...cartState.cart, action.payload],
};
} else
return {
...cartState,
cart: [...cartState.cart, action.payload],
};
}
}
};
export const CartProvider = (props) => {
const [cartState, dispatch] = useReducer(cartReducer, {
cart: [],
});
function addToCart(val) {
dispatch({
type: "add-cart",
payload: val,
});
}
Why are you insert your item even if item already exists in the cart ?
You don't have to put your action.payload in the cart if already exists
if (existItem) {
return { ...cartState };
} else {
return {
...cartState,
cart: [...cartState.cart, action.payload],
};
}

How to update an array using redux reducer, I want to change a boolean property inside an array of objects to make i true or false

I have an array, every time I fire an action it adds a new item in the array with the value true and or false, I need to change that code to return the real number of the array not adding new items
Here is my code,
import {ads} from '../../data/ads';
import {ADD_TO_FAVORITE} from '../types';
interface ActionInter {
type: string;
payload: {id: number};
}
const initialState = {
allAds: ads,
myFavorite: [],
};
const myFavorite = (state = initialState, action: ActionInter) => {
switch (action.type) {
case ADD_TO_FAVORITE:
const itemFav = state.allAds[action.payload.id - 1].isFav;
console.log(itemFav);
if (itemFav === true)
return {
...state,
allAds: [
...state.allAds,
(state.allAds[action.payload.id - 1].isFav = false),
],
};
if (itemFav === false)
return {
...state,
allAds: [
...state.allAds,
(state.allAds[action.payload.id - 1].isFav = true),
],
};
}
return state;
};
export default myFavorite;
ads, it's an array of objects
You can using map like this:
const myFavorite = (state = initialState, action: ActionInter) => {
switch (action.type) {
case ADD_TO_FAVORITE:
return {
...state,
allAds: state.allAds.map((item, index) => {
return action.payload.id - 1 === index
? {
...item,
isFav: !item.isFav,
}
: item;
}),
};
}
return state;
};
You can try this:
const myFavorite = (state = initialState, action: ActionInter) => {
switch (action.type) {
case ADD_TO_FAVORITE:
const index = action.payload.id - 1;
const newAllAds = [...state.allAds];
newAllAds[index].isFav = !newAllAds[index].isFav;
return {
...state,
allAds: newAllAds,
};
}
return state;
};
You have to use immutability, instead of updating the previous state, create a copy and update that copy
const myFavorite = (state = initialState, action: ActionInter) => {
switch (action.type) {
case ADD_TO_FAVORITE: {
// Using the spread operator we'll create a copy
const allAdsCopy = [...state.allAds];
// Identify the item index
const index = action.payload.id -1;
// Update the copy
allAdsCopy[index].isFav = !allAdsCopy[index].isFav;
return {
...state,
allAds : allAdsCopy,
};
}
default:
return state;
}
};

add key and value in array in redux

I am trying to update an array in reducer
let initialState = {
count: 0,
todos: [],
id:0,
}
const authReducer = (prevState = initialState, action) => {
switch (action.type) {
case types.ADD_TO_DO:
console.log(action.todo)
return {
...prevState,
todos: prevState.todos.concat(action.todo)
}
default:
return prevState;
}
}
And I am getting array in the form
todos:['qwerty', 'abcdef']
But I want in the form of
todos:[{id:'1', todo:'qwerty'},{id:'2',todo:'abcdef'}]
How can I achieve this?
Thanks!!!
In order to convert todos:['qwerty', 'abcdef'] to your expected format, you can map it:
var todos=['qwerty', 'abcdef'];
var result = todos.map((todo, i)=>({id:i+1, todo}));
console.log(result);
You can use reduce for this task
const todos = ['qwerty', 'abcdef']
const data = todos.reduce((acc, rec, index) => {
return [...acc, {
id: index + 1,
todo: rec
}]
}, [])
console.log(data)

redux array not updating correctly

In redux I have an array of selectedItems and changedItems
selectedItems: ["element 0","element 1","element 2"]
changedItems: ["element 6","element 7","element 8"]
<Button onClick={() => this.props.addItem(this.props.changedItems)}>save</Button>
I have a button that fires the addItem action but this is pushing 1 element into the selectedItems array from the changedItems array instead of all 3 elements and also I would like it to completely remove any existing elements in the selectedItems array when this is done
How can I push all elements from the changedItems array into the selectedItems array and overwrite the existing items in the selectedItems array?
actions.js
export const addItem = (item) => ({
type: ADD_ITEM,
item,
})
export const removeItem = (item) => ({
type: REMOVE_ITEM,
item,
})
export const clearItems = () => ({
type: CLEAR_ITEMS,
})
export const addChangedItem = (item) => ({
type: ADD_CHANGED_ITEM,
item,
})
export const removeChangedItem = (item) => ({
type: REMOVE_CHANGED_ITEM,
item,
})
selectedItemReducer.js
import {
ADD_ITEM, CLEAR_ITEMS, REMOVE_ITEM,
} from '../Constants'
const selectedItemReducer = (state = [], action) => {
switch (action.type) {
case ADD_ITEM:
return [
...state,
action.item,
]
case REMOVE_ITEM:
return state.filter((item) => item !== action.item)
case CLEAR_ITEMS:
return []
default:
return state
}
}
export default selectedItemReducer
changedItemReducer.js
import {
ADD_CHANGED_ITEM, REMOVE_CHANGED_ITEM,
} from '../Constants'
const changedItemReducer = (state = [], action) => {
switch (action.type) {
case ADD_CHANGED_ITEM:
return [
...state,
action.item,
]
case REMOVE_CHANGED_ITEM:
return state.filter((item) => item !== action.item)
default:
return state
}
}
export default changedItemReducer
If changedItems is guaranteed to always be an array, you should return those values in the reducer. From the looks of your changedItemReducer this appears to be the case.
const selectedItemReducer = (state = [], action) => {
switch (action.type) {
case ADD_ITEM:
return action.item;
case REMOVE_ITEM:
return state.filter((item) => item !== action.item)
case CLEAR_ITEMS:
return []
default:
return state
}
}
If not always an array then check first. If an array, return it, if not an array, place item into an array and return it.
const selectedItemReducer = (state = [], action) => {
switch (action.type) {
case ADD_ITEM:
return Array.isArray(action.item) ? action.item : [item];
case REMOVE_ITEM:
return state.filter((item) => item !== action.item)
case CLEAR_ITEMS:
return []
default:
return state
}
}
In both cases you are "throwing away" the old state when saving (returning) the new "changed items".

Delete Item from Array (React-Native + Redux)

I have the checklist with users and when I click on the checkbox user should add to the InputField or delete from InputField, if I check to it again.
For now works only ADD.
import ...
export default class NewEvent extends React.Component {
constructor(props) {
super(props);
this.onSelect = this.onSelect.bind(this);
}
onSelect = id => {
addMembers(id) }
findSelectedContacts = (contacts, membersArray) => {
const newArr = [];
contacts.forEach(item => {
if(membersArray.indexOf(item.id.toString()) > -1) {
newArr.push(` ${item.name}`)
}
});
return newArr;
}
render() {
const { navigation, members, location, contacts } = this.props;
const membersArray = members ? members.split(',') : [];
const selectedArray = this.findSelectedContacts(contacts, membersArray)
const inputFill = selectedArray.join().trim();
return (
<InputField
customStyle={[eventStyles.input]}
icon="addGuest"
placeholder="Add guests"
onGetText={texts => {
this.handlerChangeText(texts)
}}
value={inputFill}
/>
);
}
}
Also, I have reducer, which adds guests to input:
import { handleActions } from 'redux-actions';
import * as types from '../actions/actionTypes';
export const initialState = {
members: '',
};
const addMembers = (members, id) => {
const res = members ? `${members},${id}` : `${id}`;
return res;
}
export default handleActions(
{
[types.ADD_GUEST]: (state, action) => ({
...state,
members: addMembers(state.members, action.payload),
}),
},
initialState
);
Please advise, how I can change my reducer? I need to add or delete the user from InputFiled if I click on the ONE checkbox.
Currently, it appears that you are storing the members list as a comma-separated string. A better option would be to store the list as an actual array, and then convert that to a string when it's needed in that format, e.g. rendering.
The reducer for it might look something like this (trying to follow your existing code style:
export const initialState = {
// initialState changed to array
members: [],
};
const addMember = (members, id) => {
// add id to the end of the list
return members.concat(id);
}
const removeMember = (members, id) => {
// return a new list with all values, except the matched id
return members.filter(memberId => memberId !== id);
}
export default handleActions(
{
[types.ADD_GUEST]: (state, action) => ({
...state,
members: addMember(state.members, action.payload),
}),
[types.REMOVE_GUEST]: (state, action) => ({
...state,
members: removeMember(state.members, action.payload),
}),
},
initialState
);
And if you then need the list as a string, in your component render() method - or preferrably in your react-redux mapStateToProps selector you can convert it to a string:
memberList = state.members.join(',')

Categories

Resources