I created a cart screen and list of items using react native and redux, but when I click buy item is not adding in cart and it's also not showing any error
Below is my code where I store list of items
Jeans.js
class Jeans extends Component {
render() {
return (
<View style={styles.container}>
<FlatList
data={this.props.items}
key={(items) => items.id.toString()}
numColumns={2}
renderItem={({ item }) => (
<CardBuyItem>
<Image style={styles.image} source={item.image} />
<View style={styles.detailContainer}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.subTitle} numberOfLines={1}>
{item.subTitle}
</Text>
<Text style={styles.price}>Rs {item.price}</Text>
</View>
<TouchableHighlight onPress={() => this.props.addToCart(item.id)}>
<View style={styles.buy}>
<Text>Buy Once</Text>
</View>
</TouchableHighlight>
</CardBuyItem>
)}
/>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
items: state.clothes.jeans,
};
};
const mapDispatchToProps = (dispatch) => {
return {
addToCart: (id) => dispatch(addToCart(id)),
};
};
Below is my code of cart screen where items should added when user click by
cart.js
class Cart extends Component {
render() {
let addedItems =
this.props.items && this.props.items.length ? (
<FlatList
data={this.props.items}
key={(items) => items.id.toString()}
numColumns={2}
renderItem={({ item }) => (
<View>
<Image style={styles.image} source={item.image} />
<View style={styles.detailContainer}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.subTitle} numberOfLines={1}>
Quantity: {item.quantity}
</Text>
<Text style={styles.price}>Rs {item.price}</Text>
</View>
<TouchableOpacity>
<View style={styles.buy}>
<Text>Remove</Text>
</View>
</TouchableOpacity>
</View>
)}
/>
) : (
<View style={styles.emptyContainer}>
<Text style={styles.empty}>There is Nothing in your Cart</Text>
</View>
);
return (
<View style={styles.container}>
<View style={styles.order}>
<Text style={styles.orderText}>You Order:</Text>
</View>
<View>{addedItems}</View>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
items: state.addedItems,
};
};
And below is my code reducer and action
reducer.js
export default function ClothesReducer(state = initialstate, action) {
if (action.type === ADD_TO_CART) {
let addedItem = state.jeans.find((item) => item.id === action.id);
let existed_item = state.addedItems.find((item) => action.id === item.id);
if (existed_item) {
addedItem.quantity += 1;
return {
...state,
total: state.total + addedItem.price,
};
} else {
addedItem.quantity = 1;
let newTotal = state.total + addedItem.price;
return {
...state,
addedItems: [...state.addedItems, addedItem],
total: newTotal,
};
}
} else {
return state;
}
}
action.js
import { ADD_TO_CART } from "./ClothesActionType";
export const addToCart = (id) => {
return {
type: ADD_TO_CART,
id,
};
};
I'm trying to figure out what's wrong but can't find any error. Can someone help me to fix this?
In cart.js you should replace this
const mapStateToProps = (state) => {
return {
items: state.addedItems,
};
};
With
const mapStateToProps = (state) => {
return {
items: state.clothes.addedItems,
};
};
You are mutating state, here is some info on how to not do that.
I think your reducer should look something like this:
export default function ClothesReducer(
state = initialstate,
action
) {
if (action.type === ADD_TO_CART) {
let addedItem = state.jeans.find(
(item) => item.id === action.id
);
let existed_item = state.addedItems.find(
(item) => action.id === item.id
);
const addedItems = existed_item
? state.addedItems.map((item) =>
item === existed_item
? { ...item, quantity: item.quantity + 1 }
: item
)
: [
...state.addedItems,
{ ...addedItem, quantity: 1 },
];
return {
...state,
addedItems,
total: state.total + addedItem.price,
};
} else {
return state;
}
}
Related
I test from itemList up to the reducer and action, it works fine and it delete the item I want to be deleted but I got this error after that. I wonder what I'm doing wrong here. Can anyone help me what should I do?
// ItemList.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { selectAllItems } from '../../../redux/reducer/itemReducer';
import { deleteItem } from '../../../redux/action/ItemAction';
import AppButton from '../../shared/AppButton';
const ItemList = () => {
const dispatch = useDispatch();
const itemData = useSelector(selectAllItems);
function removeHandler(id){
dispatch(deleteItem(id));
}
return (
<>
{itemData.map((item) => (
<View style={styles.itemListContainer} key={item.id}>
<Text style={styles.textStyle}>{item.id}</Text>
<Text style={styles.textStyle}>{item.name}</Text>
<Text style={styles.textStyle}>{item.price}</Text>
<Text style={styles.textStyle}>{item.availableItem}</Text>
<View style={styles.operationContainer}>
<AppButton title="Edit" buttonStyle={buttonStyle.edit} textStyle={buttonStyle.text}/>
<AppButton title="Remove" buttonStyle={buttonStyle.remove} textStyle={buttonStyle.text} onPress={() => removeHandler(item.id)}/>
</View>
</View>
))}
</>
)
}
// ItemReducer.js
const itemReducer = (state = initialState, { type, payload } = action) =>{
switch (type) {
case ADD_ITEM:
return { ...state, payload };
case DELETE_ITEM:
return { payload };
default:
return state;
}
}
// itemAction.js
function DELETE_ITEM(filtered){
return{
type: 'DELETE_ITEM',
payload: filtered
}
}
export function deleteItem(id){
return function(dispatch, getState){
const itemData = getState().item;
const filtered = itemData.filter((item) => item.id !== id);
dispatch(DELETE_ITEM(filtered));
}
}
Please check your itemData. Sure that is not an array. For sure, you can write the following:
const data = Array.isArray(itemData) ? itemData : []
...
{data.map((item) => (
<View style={styles.itemListContainer} key={item.id}>
<Text style={styles.textStyle}>{item.id}</Text>
<Text style={styles.textStyle}>{item.name}</Text>
<Text style={styles.textStyle}>{item.price}</Text>
<Text style={styles.textStyle}>{item.availableItem}</Text>
<View style={styles.operationContainer}>
<AppButton title="Edit" buttonStyle={buttonStyle.edit} textStyle={buttonStyle.text}/>
<AppButton title="Remove" buttonStyle={buttonStyle.remove} textStyle={buttonStyle.text} onPress={() => removeHandler(item.id)}/>
</View>
</View>
))}
function Carts() {
let cartEval = [];
const cartData = useSelector((state) => state.cartProducts);
cartEval = cartData.map((item) => evalCart(item));
function evalCart(a) {
if (cartEval.find((b) => b.id == a.id)) {
return { ...b, numOfTimes: numOfTimes + 1 };
} else {
return {
id: a.id,
numOfTimes: 1,
prodTitle: a.title,
price: a.price,
};
}
}
const totalPrice = cartData
.map((item) => item.price)
.reduce((x, y) => x + y, 0);
function cartRender({ item }) {
return (
<View>
<View>
<View>
<Text>{item.numOfTimes}</Text>
<Text>{item.prodTitle}</Text>
<Text>{item.price}</Text>
<Text>Delete Button</Text>
</View>
</View>
</View>
);
}
return (
<View>
<View>
<Text>Total Sum of Items:{totalPrice}</Text>
</View>
<FlatList data={cartEval} renderItem={cartRender} />
</View>
);
}
const styles = StyleSheet.create({});
export default Carts;
This is what I want to achieve:
I need the function evalCart(a) to check if the item.id exist in the cartEval. If the id exists, increase item.nuofItems by 1. If it does not, create a new item with the id in the cartEval array.
But every time the function is called it returns false, hence the first part of the if statement never executes. only the else part. What am I doing wrong?
I was finally able to solve the puzzle. This is my solution below
function Carts() {
let cartEval = [];
const cartData = useSelector((state) => state.cartProducts);
cartData.map((item) => evalCart(item));
function evalCart(a) {
if (cartEval.find((b) => b.id == a.id)) {
const dummy = cartEval.filter((d) => d.id == a.id)[0];
cartEval.splice(cartEval.indexOf(dummy), 1, {
...dummy,
numOfTimes: dummy.numOfTimes + 1,
});
} else {
cartEval.push({
id: a.id,
numOfTimes: 1,
prodTitle: a.title,
price: a.price,
});
}
}
const totalPrice = cartData
.map((item) => item.price)
.reduce((x, y) => x + y, 0);
function cartRender({ item }) {
return (
<View>
<View>
<View>
<Text>{item.numOfTimes}</Text>
<Text>{item.prodTitle}</Text>
<Text>{item.price}</Text>
<Text>Delete Button</Text>
</View>
</View>
</View>
);
}
return (
<View>
<View>
<Text>Total Sum of Items:{totalPrice}</Text>
</View>
<FlatList data={cartEval} renderItem={cartRender} />
</View>
);
}
const styles = StyleSheet.create({});
export default Carts;
Thanks for your contributions
I made a Flatlist and and edit button that can open a modal to a text input and track down the key of the specific item. How do I update this item in the Flatlist with the different text input. I tried doing something with the setJournal but I don't know how to return it with the edited entry.
export default function HomeScreen({ navigation }) {
const [journal, setJournal] = useState([
{ date: "12-dec22", entry: "good day", key: "1" },
{ date: "12-dec22", entry: "bad day", key: "2" },
]);
const [editModal, setEditModal] = useState(false);
const handleEditPress = (key) => {
const currentJournal = journal.find((journn) => {
return journn.key === key;
});
setEditModal(true);
console.log(key);
};
const updateEntry = (key, entry) => {
if (journal.key === key) {
setJournal((currentJournal) => {
return [entry, ...currentJournal];
});
} else {}
journal = journal.map((journn) =>
journn.key === key ? { ...journn, ...updateEntry } : journn
);
};
return (
<View>
<Modal visible={editModal}>
<TextInput onChangeText={() => updateEntry()} />
<MaterialIcons name="close" onPress={() => setEditModal(false)} />
</Modal>
<View>
<MaterialIcons onPress={() => setModalOpen(true)}/>
<MaterialIcons onPress={() => deleteAll()} />
</View>
<FlatList
data={journal}
renderItem={({ item }) => (
<View style={styles.flatlistView}>
<TouchableOpacity>
<View>
<MaterialIcons onPress={() => handleEditPress(item.key)}/>
</View>
</TouchableOpacity>
</View>
)}
/>
</View>
);
}
const handleEditPress = (editedField, itemKey)=>{
journal.filter((currentItem.key)=>{(currentItem.key) === itemKey})).text = editedField
setJournal(journal)
}
I'm making an app that have notes, and when develop the delete function, i faced this error, the useState do not update when use alongside with redux dispatch function ( even the redux function run, the useState do not run ) , i tried to create the same issue on codesandbox, but weird is it WORKING TOTALLY FINE ON WEB?!
Here is the code:
NoteList.tsx
function NoteList(props: noteListI) {
const { title, note, id, date, selectStatus } = props; //they are props
const nav = useNavigation(); //for navigation
const [isDeleteChecked, setDeleteChecked] = useState(false);
const dispatch = useDispatch();
const data = useSelector((state: RootState) => state.persistedReducer.note); // note item from redux
const toggleSelectedButton = useSelector(
(state: RootState) => state.toggle.enableSelectedButton
); // to show selected button icon
const onNavDetail = () => {
nav.navigate(RouteName.EDIT_NOTE, {
date: date,
note: note,
header: title,
id: id,
});
};
const toggleSelectButton = () => {
dispatch(switchToggle());
}; // toggle delete button function
const setDeleteItem = () => {
setDeleteChecked(!isDeleteChecked);
dispatch(toggleSelect({ id: id }));
}; ////==>>> the issue here the 'setDeleteChecked' not even work
return (
<TouchableOpacity
onLongPress={() => {
toggleSelectButton();
}}
style={CONTAINER}
onPress={() => (!toggleSelectedButton ? onNavDetail() : setDeleteItem())}
>
<View style={NOTE_ITEM_CONTAINER}>
<Text>{isDeleteChecked?.toString()}</Text> ==>always false, why????!
<View>
<View row centerV style={HEADER_CONTAINER}>
<View>
<AppText bold style={HEADER_TEXT}>
{title}
</AppText>
</View>
{toggleSelectedButton && (
<View>
{selectStatus ? ( ===> this is from redux and work but slow
<CheckIcon name="checkcircle" size={size.iconSize} />
) : (
<CheckIcon name="checkcircleo" size={size.iconSize} />
)}
</View>
)}
</View>
<View style={NOTE_CONTAINER}>
<AppText numberOfLines={7}>{note}</AppText>
</View>
</View>
<View
style={{
alignSelf: "flex-end",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<AppText>{moment(date).format("h:mmA MMM Do YY")}</AppText>
</View>
</View>
</TouchableOpacity>
);
}
export default memo(NoteList);
I use these from flatlist, here is the main flatlist code:
export default function NoteListScreen() {
const [user, setUser] = useState<any>();
const nav = useNavigation();
// useEffect(() => {
// dispatch(loadDefault());
// }, []);
const dispatch: AppDispatch = useDispatch();
const data = useSelector((state: RootState) => state.persistedReducer.note);
const userInfo: user = useSelector(
(state: RootState) => state.persistedReducer.firebase.userInfomation
);
useEffect(() => {
dispatch(fetchNote(userInfo.email)); //fetch note from firebase
}, []);
return (
<SafeAreaView style={CONTAINER}>
{data.length === 0 ? (
<>
<ScrollView>
<HeaderNote />
<AppText style={EMPTY_NOTE}>
Hmm, so don't have any secret yet
</AppText>
</ScrollView>
<FooterNote />
</>
) : (
<View style={CONTAINER}>
<FlatList
removeClippedSubviews
data={data}
style={{
marginBottom:
Platform.OS === "ios"
? onePercentHeight * 15
: onePercentHeight * 12,
}}
keyExtractor={() => {
return (
new Date().getTime().toString() +
Math.floor(
Math.random() * Math.floor(new Date().getTime())
).toString()
);
}}
ListHeaderComponent={() => <HeaderNote />}
renderItem={({ item, index }) => {
return (
<NoteList ==> here , the note list that faced error
note={item.note}
title={item.header}
date={item.date}
id={item.id}
selectStatus={item.selectStatus}
/>
);
}}
/>
<FooterNote />
</View>
)}
</SafeAreaView>
);
}
Here is the reducer code:
const noteReducer = createSlice({
name: "note",
initialState: NoteList,
reducers: {
addNote: (state, action: PayloadAction<NoteI>) => {
const newNote: NoteI = {
id:
new Date().getTime().toString() +
Math.floor(
Math.random() * Math.floor(new Date().getTime())
).toString(),
header: action.payload.header,
note: action.payload.note,
date: new Date(),
selectStatus: false,
};
state.push(newNote);
},
toggleSelect: (state, action: PayloadAction<NoteI>) => {
return state.map((item) => {
if (item.id === action.payload.id) {
return { ...item, selectStatus: !item.selectStatus };
}
return item;
});
}, ///========>This is the reducer using in the note function
loadDefault: (state) => {
return state.map((item) => {
return { ...item, selectStatus: false };
});
},
resetNote: (state) => {
return (state = []);
},
editNote: (state, action: PayloadAction<NoteI>) => {
return state.map((item) => {
if (item.id === action.payload.id) {
return {
...item,
note: action.payload.note,
header: action.payload.header,
date: action.payload.date,
};
}
return item;
});
},
},
extraReducers: (builder) => {
builder.addCase(fetchNote.fulfilled, (state, action) => {
state = [];
return state.concat(action.payload);
});
},
});
Here is the image of what i'm talking about, the code in image from noteList.tsx, the first piece of code i post here
Here is the quick gif:
In above gif, the false must return true then false everytime i click ( as above code ) but i don't why it never change value, the black dot also change color because it use value using in the same function using with this value, but when setDeleteItem fire, it NOT fire the setDeleteChecked(!isDeleteChecked)
Here is the demo that i made, but it WORK TOTALLY FINE, but in my app, it make weird error https://codesandbox.io/s/nostalgic-neumann-0497v?file=/redux/some-redux.tsx
Please help, i'm trying to provide must as i can, i stuck for days for this, thank you so much, if you need any detail, just tell me
I created a button in flat list, when user click an specific item, it's button should change state and increment button should appear, but button changing state for all the items. I pass id too but it's not working, can someone please help me... below is my code
Items.js
<FlatList
data={this.props.items}
extraData={this.props}
keyExtractor={(items) => items.id.toString()}
numColumns={2}
renderItem={({ item }) => (
<CardBuyItem>
<Image style={styles.image} source={item.image} />
<View style={styles.detailContainer}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.subTitle} numberOfLines={1}>
{item.subTitle}
</Text>
<Text style={styles.price}>Rs {item.price}</Text>
</View>
{this.props.button && this.props.added.length > 0 ? (
<View style={styles.add}>
<Text style={styles.quantity}>{item.quantity}</Text>
<MaterialCommunityIcons
style={styles.iconUp}
size={20}
name="plus-circle-outline"
onPress={() => this.props.addQuantity(item.id)}
/>
<MaterialCommunityIcons
style={styles.iconDown}
size={20}
name="minus-circle-outline"
onPress={() => this.props.subtractQuantity(item.id)}
/>
</View>
) : (
<View style={styles.buy}>
<Text
style={styles.buyonce}
onPress={() => {
this.props.addToCart(item.id);
this.props.showCart();
this.props.showButton(item.id);
}}
>
Buy Once
</Text>
</View>
)}
</CardBuyItem>
)}
/>
const mapStateToProps = (state) => {
return {
items: state.clothes.jeans,
button: state.clothes.showButton,
added: state.clothes.addedItems,
};
};
const mapDispatchToProps = (dispatch) => {
return {
addToCart: (id) => dispatch(addToCart(id)),
addQuantity: (id) => dispatch(addQuantity(id)),
subtractQuantity: (id) => dispatch(subtractQuantity(id)),
showCart: () => dispatch(showCart()),
showButton: (id) => dispatch(showButton(id)),
};
};
That's my item list with mapStateToProsp and mapDispatchToProps here button should change it's state
reducer.js
if (action.type === SHOW_BUTTON) {
let addedItem = state.jeans.find((item) => item.id === action.id);
return {
...state,
addedItem: addedItem,
showButton: action.showButton,
};
}
const initialstate = { showButton: false}
it's my reducer function with initial state of that button
action.js
export const showButton = (id) => {
return {
type: SHOW_BUTTON,
showButton: true,
id,
};
};
it's my action where I'm describing action for my reducer
You are having a common state variable for this which causes it to show all buttons.
You can do a simple solution like this.
In your flatlist you can have a logic to display the button
{this.props.added.find(x=>x.id==item.id) !=null ? (
Or if you have to use the reducer, you will have to have a property in the array and update it which would be complex to maintain.