useReducer incrementing / decrementing 2 instead of 1 - reactjs

I'm just trying to learn the useReducer hook in react. Just playing around. Can someone help with an issue I'm having? When I click the button to "plus a unit" or "minus" a unit it takes 2 away. Any suggestions?
Perhaps map. is a bad method for updating the state in the reducer?
Test code below. I've put it all into one component for purpose of below, rather than posting all the context stuff and children.
Any help much appreciated :)
import React, {useReducer} from 'react'
function Test() {
const itemFile = [
{sku: '123456', description: 'item 1', stockavailable: 5},
{sku: '654321', description: 'item 2', stockavailable: 1},
{sku: '666666', description: 'item 3', stockavailable: 0},
]
const reducer = (state,action) => {
switch (action.type) {
case 'DELETE_LINE':
let newstate = state.filter(item => item.sku !== action.payload)
return newstate
case 'MINUS_ONE_UNIT':
return state.map(item => {
if(item.sku === action.payload) {
item.stockavailable = item.stockavailable-1;
}
return item;
})
case 'PLUS_ONE_UNIT':
return state.map(item => {
if(item.sku === action.payload) {
item.stockavailable = item.stockavailable+1;
}
return item;
})
default:
return state
}
}
const [liveItemFile, dispatch] = useReducer(reducer, itemFile);
return (
<>
{liveItemFile.map((item) => (
<div key={item.sku}>
{item.sku}: {item.description} - {item.stockavailable}
<button onClick={() => dispatch({type: 'DELETE_LINE', payload: item.sku})}>
DELETE LINE
</button>
<button onClick={() => dispatch({type: 'MINUS_ONE_UNIT', payload: item.sku})}>
Minus Unit
</button>
<button onClick={() => dispatch({type: 'PLUS_ONE_UNIT', payload: item.sku})}>
Plus Unit
</button>
</div>
))}
</>
);
}
export default Test;

Move your reducer's logic and your initial state outside of your component as with each render they get evaluated:
import React, { useReducer } from "react";
const itemFile = [
{ sku: "123456", description: "item 1", stockavailable: 5 },
{ sku: "654321", description: "item 2", stockavailable: 1 },
{ sku: "666666", description: "item 3", stockavailable: 0 },
];
const reducer = (state, action) => {
switch (action.type) {
case "DELETE_LINE":
let newstate = state.filter((item) => item.sku !== action.payload);
return newstate;
case "MINUS_ONE_UNIT":
return state.map((item) => {
if (item.sku === action.payload) {
item.stockavailable = item.stockavailable - 1;
}
return item;
});
case "PLUS_ONE_UNIT":
return state.map((item) => {
if (item.sku === action.payload) {
item.stockavailable = item.stockavailable + 1;
}
return item;
});
default:
return state;
}
};
function Test() {
const [liveItemFile, dispatch] = useReducer(reducer, itemFile);
return (
<>
{liveItemFile.map((item) => (
<div key={item.sku}>
{item.sku}: {item.description} - {item.stockavailable}
<button onClick={() => dispatch({ type: "DELETE_LINE", payload: item.sku })}>DELETE LINE</button>
<button onClick={() => dispatch({ type: "MINUS_ONE_UNIT", payload: item.sku })}>Minus Unit</button>
<button onClick={() => dispatch({ type: "PLUS_ONE_UNIT", payload: item.sku })}>Plus Unit</button>
</div>
))}
</>
);
}
export default Test;

Related

Iam making a Todo using (useReducer and useContext) but the update functionality is not working please check this out

This is my App.jsx , where iam displaying all the todos, after clicking edit the input gets the value of selected todo and while changing the value and clicking on update it does nothing.
export default function App() {
const { dispatch, state } = useContext(todoContext);
const [todo, setTodo] = useState("");
const [isEditing, setIsEditing] = useState(false);
const [selectedTodo, setSelectedTodo] = useState("");
const handleSubmit = (e) => {
const id = uuidv4();
e.preventDefault();
if (!isEditing) {
dispatch({
type: "ADD_TODO",
payload: { id: id, text: todo },
});
setTodo("");
} else {
dispatch({
type: "UPDATE_TODO",
payload: selectedTodo,
});
setIsEditing(false);
setTodo("");
}
};
const handleEdit = (val) => {
setIsEditing(true);
const item = state.todos.find((todo) => todo.id === val.id);
setTodo(item.text);
setSelectedTodo(item);
};
return (
<div className="App">
<br />
<h1>Hello World This is Todo App</h1>
<br />
<form onSubmit={handleSubmit}>
<input
value={todo}
onChange={(e) => setTodo(e.target.value)}
type="text"
placeholder="Enter a todo"
/>
<button>{isEditing ? "Update" : "Submit"}</button>
</form>
{state.todos.map((item) => (
<div key={item.id} className="todoitem">
<p
onClick={() => dispatch({ type: "TOGGLE_TODO", payload: item.id })}
style={{
cursor: "pointer",
textDecoration: item.completed ? "line-through" : "",
}}
>
{item.text}
</p>
<span
onClick={() => dispatch({ type: "REMOVE_TODO", payload: item.id })}
>
×
</span>
<button onClick={() => handleEdit(item)}>Edit</button>
</div>
))}
</div>
);
}
This is my reducers (todoReducer.jsx) , where the upadte functionality doesnot work when i click edit to change the value and click update it does nothing
export const INITIAL_STATE = {
todos: [],
updated: [],
};
export const reducer = (state, action) => {
switch (action.type) {
case "ADD_TODO":
return {
...state,
todos: [...state.todos, action.payload],
};
case "REMOVE_TODO": {
return {
...state,
todos: state.todos.filter((item) => item.id !== action.payload),
};
}
case "TOGGLE_TODO":
return {
...state,
todos: state.todos.map((todo) =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
),
};
case "UPDATE_TODO":
return {
...state,
todos: state.todos.map((todo) => {
if (todo.id === action.payload.id) {
return action.payload;
}
return todo;
}),
};
default:
return state;
}
};
you need to update text with latest text
selectedTodo has old data
todo have new text
in your handleSubmit please update below code
dispatch({
type: "UPDATE_TODO",
payload: { ...selectedTodo, text: todo },
});

How do I create a delete/clear button in REACT js?

Hi I'm new to REACT and I have a HW where I need to create a grocery shopping list and I need to create a clear button. The isPurchased key value pair is a boolean though. I need to create a button that when I click Purchased it clears that grocery item off my list. Any help would be appreciated.
class App extends Component {
state = {
grocery: grocery,
item: '',
brand: '',
units: Number,
quantity: Number,
isPurchased: Boolean
}
handleChange = (e) => {
this.setState({ [e.target.id]: e.target.value })
}
handleSubmit = (e) => {
e.preventDefault()
const addGrocery = {
item: this.state.item,
brand: this.state.brand,
units: this.state.units,
quantity: this.state.quantity,
}
this.setState({
grocery: [addGrocery, ...this.state.grocery],
item: '',
brand: '',
units: Number,
quantity: Number,
})
const removeGrocery = {
item: this.state.item
}
}
hey here is a full code for creating a to do list in react (it will be very similar to your problem):
**
Summary
** of the idea of creating a to-do list or shopping list is that each to-do will be an object, when we create a new object we will insert it into an array. once it is in the array by using the array.map() function we will convert each object to an HTML element to make the UI.
if something is unclear I am here to answer
file - App.js:
import React, { useState, useReducer } from "react";
import Todo from "./Todo";
export const ACTIONS = {
ADD_TODO: "add-todo",
TOGGLE_TODO: "toggle-todo",
DELETE_TODO: "delete-todo",
};
function reducer(todos, action) {
switch (action.type) {
case ACTIONS.ADD_TODO:
return [...todos, newTodo(action.payload.name)];
case ACTIONS.TOGGLE_TODO:
return todos.map((todo) => {
if (todo.id === action.payload.id) {
return { ...todo, complete: !todo.complete }; //change to complete if we found to id that toggled
}
return todo;
});
case ACTIONS.DELETE_TODO:
return todos.filter((todo) => todo.id !== action.payload.id);
default:
return todos;
}
}
function newTodo(name) {
return { id: Date.now(), name: name, complete: false };
}
const App = () => {
const [todos, dispatch] = useReducer(reducer, []); //useReducer return the state and the reducer function
const [name, setName] = useState("");
function handleSubmit(e) {
e.preventDefault();
dispatch({ type: ACTIONS.ADD_TODO, payload: { name: name } });
setName("");
}
return (
<>
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</form>
{todos.map((todo) => {
return <Todo key={todo.id} todo={todo} dispatch={dispatch} />;
})}
</>
);
};
export default App;
Another file (component) - Todo.js:
import React from "react";
import { ACTIONS } from "./App";
const Todo = ({ todo, dispatch }) => {
return (
<div>
<span style={{ color: todo.complete ? "#AAA" : "#000" }}>
{todo.name}
</span>
<button
onClick={() =>
dispatch({ type: ACTIONS.TOGGLE_TODO, payload: { id: todo.id } })
}
>
Toggle
</button>
<button
onClick={() =>
dispatch({ type: ACTIONS.DELETE_TODO, payload: { id: todo.id } })
}
>
Delete
</button>
</div>
);
};
export default Todo;

Add product with Attributes to cart using redux

I need help in Reactjs app 'Add to cart'. I am using redux and to add products to cart with quantity and everything going right until tried to add specific attributes such as size or color and I don't know how to handle this can anyone explain me the logic to do it?
this is the reducer
case actionTypes.ADD_TO_CART:
const item = state.data.find((item) => item.id === action.payload.id);
const inCart = state.cart.find((item) =>
item.id === action.payload.id ? true : false
);
return {
...state,
cart: inCart
? state.cart.map((item) =>
item.id === action.payload.id
? {
...item,
qty: item.qty + 1,
selectedAttributes: [...item.selectedAttributes]
}
: item
)
: [...state.cart, {
...item, qty: 1,
selectedAttributes: []
}],
};
case actionTypes.ADJUST_ATTRIBUTES:
let selectedAttributes = [];
return {
...state,
cart: state.data.map((item) =>
item.id === action.id
? {
...item,
selectedAttributes: { [action.selectedAttributes.name]: action.selectedAttributes.value }
}
: item
),
};
this is action
export const addToCart = (itemID) => {
return {
type: actionTypes.ADD_TO_CART,
payload: {
id: itemID
}
}
}
export const selectAttribute = (itemID, name, value) => {
return {
type: actionTypes.ADJUST_ATTRIBUTES,
id: itemID,
selectedAttributes: {
name: name,
value: value
}
}
}
this is the radio button I want to select from it to add attribute
attribute.items.map((item, index2) => {
return (
<div className="button" key={index2}>
<input
onClick={() =>
this.props.selectAttribute(
this.props.currentItem.id,
attribute.name,
item.value
)
}
type="radio"
disabled={!this.props.currentItem.inStock}
name={`${this.props.currentItem.name}-${attribute.name}-${index}`}
value={item.value}
className="attributes-value"
/>
<label
className="attributes-label"
htmlFor={`${this.props.currentItem.name}-${attribute.name}-${index}`}
>
{item.value}
</label>
</div>
);
})

How to mock userReducer with jest?

I have the below code:
import ReactDOM from "react-dom";
const initialTodos = [
{
id: 1,
title: "Todo 1",
complete: false,
},
{
id: 2,
title: "Todo 2",
complete: false,
},
];
const reducer = (state, action) => {
switch (action.type) {
case "COMPLETE":
return state.map((todo) => {
if (todo.id === action.id) {
return { ...todo, complete: !todo.complete };
} else {
return todo;
}
});
default:
return state;
}
};
function Todos() {
const [todos, dispatch] = useReducer(reducer, initialTodos);
const handleComplete = (todo) => {
dispatch({ type: "COMPLETE", id: todo.id });
};
return (
<>
{todos.map((todo) => (
<div key={todo.id}>
<label>
<input
type="checkbox"
checked={todo.complete}
onChange={() => handleComplete(todo)}
/>
{todo.title}
</label>
</div>
))}
</>
);
}
ReactDOM.render(<Todos />, document.getElementById("root"));
I am trying to test this component with jest. How to mock the useReducer hook here so that I can change the state of the component manually rather than changing it by clicking the checkbox.
I have tried some examples but unfortunately it did not work. Please suggest.

On button click check the checkbox and add the data into cart in react redux?

In this problem what can I do so that the on clicking the button, both the function add to the cart and select the checkbox executes together? In the current scenario add to cart is working when the button is clicked but the checkbox isn't selected. I removed all the styles so that the actual code is readable
YourItems.js
import React from "react";
import { connect } from "react-redux";
import { addOn } from "./data";
import { addOnhandleChange,addOnSelector} from "./AddOnActions";
const YourItems = ({ addOnhandleChange, addOnSelector, selectedId }) => {
return (
<div>
{addOn.map(({ id, name, img, price, unit }) => {
return (
<div key={id}>
<div>
<img src={img} alt={name} />
<p>{name}</p>
<span>Rs. {price}</span>
<input
type="checkbox"
checked={id === selectedId}
onChange={() => addOnhandleChange(id)}
/>
</div>
<button onClick={() =>addOnSelector({id, name,img,price,unit, })}>
Add
</button>
</div>
)})}
</div>
);
};
const mapStateToProps = (state) => {
return {
selectedId: state.addOn.selectedId,
};
};
export default connect(mapStateToProps, { addOnSelector,addOnhandleChange})(YourItems);
AddOnAction.js
export const addOnhandleChange = (id) => (dispatch) => {
dispatch({
type: "SELECTED_ID",
payload: id,
});
};
export const addOnSelector = ({ id, name, img, price, unit }) => (dispatch) => {
dispatch({
type: "ADD_TO_CART",
payload: { id, name, img, price, unit },
});
};
reducer.js
const initialState = {
selectedId: "",
};
export default function SelectorReducer(
state = initialState,
action
) {
switch (action.type) {
case "SELECTED_ID":
return {
...state,
selectedId: action.payload,
};
default:
return state;
}
}
data.js
export const addOn = [
{
id: 12654,
img: "https://images.pexels.com/photos/1132047/pexels-photo-1132047.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
name: "Banana",
price: 10,
unit: 1,
},
{
id: 2256435,
img: "https://images.pexels.com/photos/1132047/pexels-photo-1132047.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
name: "Mango",
price: 20,
unit: 1,
},
{
id: 3429684,
img: "https://images.pexels.com/photos/1132047/pexels-photo-1132047.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
name: "Grape",
price: 30,
unit: 1,
},
];
Add a case for "ADD_TO_CART" action type and use the id packed in action.payload in the reducer.
export default function SelectorReducer(
state = initialState,
action
) {
switch (action.type) {
case "SELECTED_ID":
return {
...state,
selectedId: action.payload,
};
case "ADD_TO_CART":
return {
...state,
selectedId: action.payload.id, // <-- use the payload.id
};
default:
return state;
}
}

Resources