I am trying to implement the React Accordion project using React-Redux.
Here is my Reducer code where I have a map function to perform operations one by one on every id:
import * as actionTypes from '../actions/actions';
const initialState = {
active: [
{ id: 1, status: false },
{ id: 2, status: false },
{ id: 3, status: false },
{ id: 4, status: false }
]
}
const reducer = (state = initialState, action) => {
switch(action.type) {
case actionTypes.ACTIVE_STATE:
return {
...state,
active: state.active.map((acdnobj) => {
const panel = document.querySelector(`.panel-${acdnobj.id}`);
return {
...acdnobj,
acdnobj: acdnobj.id === parseInt(action.id) ? (
acdnobj.status = true,
panel.style.maxHeight = panel.scrollHeight + "px"
) : (
acdnobj.status = false,
panel.style.maxHeight = '0px'
)
}
})
}
default:
}
return state;
}
export default reducer;
And this is my Accordion where I have another map function to increase the id numbers:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actionTypes from '../store/actions/actions';
class Accordion extends Component {
localDispatch = (key) => {
this.props.expandAccordion(key.target.value);
}
render() {
return (
<div>
{this.props.accordions.map((accordion, index) => {
return (
<div key={index}>
<button value={ index + 1 } className={`accordion ${accordion.status}`}
onClick={this.localDispatch.bind(this)}>
{this.props.title}
</button>
<div className={`panel panel-${accordion.id}`}>
{this.props.content}
</div>
</div>
)
})}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
accordions: state.data.active
};
}
const mapDispatchToProps = (dispatch) => {
return {
expandAccordion: (key) => dispatch({type: actionTypes.ACTIVE_STATE, id: key})
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Accordion);
And in App.js component where I have another map function to fetch data from an excel file:
if(list.length > 0) {
accordionDIV = list[0].map((d, index) => (
<Accordion _key={index}
title = {
<Table>
<tr key={d.ID}>
<td>{d.ID}</td>
<td>{d.Mail}</td>
<td>{d.Name}</td>
<td>{d.PhoneNo}</td>
<td>{d.City}</td>
<td>{d.Date}</td>
<td>{d.Time}</td>
</tr>
</Table>
}
content = {
<div>
<p className="header">
<span style={{color:"#3c67a5"}}>Shipping Address:</span>
292 Naqashband Colony, Near rabbania Mosque, Multan
</p>
<Table size="sm" className="content">
<thead>
<tr>
<th style={{width:"10%"}}></th>
<th style={{width:"15%"}}>Article No</th>
<th style={{textAlign:"left", width:"30%"}}>Product Name</th>
<th style={{width:"15%"}}>Quantity</th>
<th style={{width:"15%"}}>Price</th>
<th style={{width:"20%"}}>Total Amount</th>
</tr>
</thead>
<tbody>
{list[1].map((c) =>
c.ID === d.ID ? (
<tr key={c.ID}>
<td> <FontAwesomeIcon icon={faTrashAlt} size="xs" className="icon" /> </td>
<td>{c.ArticleNo}</td>
<td style={{textAlign:"left"}}>{c.ProductName}</td>
<td>{c.Quantity}</td>
<td>{c.Price}</td>
<td>{c.TotalAmount}</td>
</tr>
) : null
)}
</tbody>
</Table>
</div>
}
/>
))
}
The problem is that in the run time I have multiple accordions for the same data.
Suggest me what can I do for now to solve this problem.
import * as actionTypes from '../actions/actions';
const initialState = {
active: [
{ id: 1, status: false },
{ id: 2, status: false },
{ id: 3, status: false },
{ id: 4, status: false }
]
}
const reducer = (state = initialState, action) => {
switch(action.type) {
case actionTypes.ACTIVE_STATE:
let newActive = [...state.active];
newActive = newActive.map((acdnobj) => {
const panel = document.querySelector(`.panel-${acdnobj.id}`);
panel.style.maxHeight = (acdnobj.id === parseInt(action.id)) ? panel.scrollHeight + "px" : '0px';
return {
...acdnobj,
status: acdnobj.id === parseInt(action.id)
}
})
return {
...state,
active: newActive
}
default:
}
return state;
}
export default reducer;
This could be the array mutable issue on Javascript so you can use the above code or try with immutable.js
Related
delete product from database and table (mern project)
this is my code. but the product is not being deleted and showing this error - xhr.js:210 DELETE http://localhost:3000/api/v1/admin/product/24e74a20a5e5e8574e231a0 404 (Not Found)
I'm trying but I can't solve the problem. Please give me some advice.
productRoute.js
router.route('/admin/product/:id')
.put(isAuthenticatedUser,authorizeRoles("admin"), updateProduct)
.post(isAuthenticatedUser,authorizeRoles("admin"), deleteProduct)
productController.js
exports.deleteProduct = catchAsyncError(async(req,res,next) => {
const product = await Product.findById(req.params.id)
if(!product){
return next(new ErrorHandler("Product not found",404));
}
await product.remove();
res.status(200).json({
success:true,
message:"product deleted successfully"
})
})
productConstant.js
export const DELETE_PRODUCT_REQUEST = "DELETE_PRODUCT_REQUEST";
export const DELETE_PRODUCT_SUCCESS = "DELETE_PRODUCT_SUCCESS";
export const DELETE_PRODUCT_RESET = "DELETE_PRODUCT_RESET";
export const DELETE_PRODUCT_FAIL = "DELETE_PRODUCT_FAIL";
export const CLEAR_ERROR ="CLEAR_ERROR";
productAction.js
export const deleteProduct = (id) => async (dispatch) => {
try {
dispatch({ type: DELETE_PRODUCT_REQUEST });
const { data } = await axios.delete(`/api/v1/admin/product/${id}`);
dispatch({
type: DELETE_PRODUCT_SUCCESS,
payload: data.success,
});
} catch (error) {
dispatch({
type: DELETE_PRODUCT_FAIL,
payload: error.response.data.message,
});
}
};
productReducer.js
export const productsReducer = (state ={products: []},action) => {
switch(action.type){
case ALL_PRODUCTS_REQUEST:
case ADMIN_PRODUCT_REQUEST:
return{
loading:true,
products:[]
}
case ALL_PRODUCTS_SUCCESS:
return{
loading:false,
products:action.payload.products,
productCount:action.payload.productCount
}
case ADMIN_PRODUCT_SUCCESS:
return{
loading:false,
products:action.payload,
}
case ALL_PRODUCTS_FAIL:
case ADMIN_PRODUCT_FAIL:
return{
loading:false,
error:action.payload
}
case CLEAR_ERROR:
return{
...state,
error:null
}
default:
return state;
}
};
export const productReducer = (state = {}, action) => {
switch (action.type) {
case DELETE_PRODUCT_REQUEST:
return {
...state,
loading: true,
};
case DELETE_PRODUCT_SUCCESS:
return {
...state,
loading: false,
isDeleted: action.payload,
};
case DELETE_PRODUCT_FAIL:
return {
...state,
loading: false,
error: action.payload,
};
case DELETE_PRODUCT_RESET:
return {
...state,
isDeleted: false,
};
case CLEAR_ERROR:
return {
...state,
error: null,
};
default:
return state;
}
};
store
const reducer = combineReducers({
products:productsReducer,
product: productReducer,
})
ProductList.js
import React, { Fragment, useEffect } from 'react'
import { useSelector, useDispatch } from "react-redux";
import SideBar from "./Sidebar";
import {
clearErrors,
getAdminProduct,
deleteProduct
} from "../../Actions/productAction";
import { Link,} from "react-router-dom";
import './ProductList.css'
import { DELETE_PRODUCT_RESET } from "../../Constants/productConstant";
import { useNavigate } from 'react-router';
const ProductList = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { error, products } = useSelector((state) => state.products);
const { error: deleteError, isDeleted } = useSelector(
(state) => state.product
);
useEffect(() => {
if (error) {
alert(error);
dispatch(clearErrors());
}
if (deleteError) {
alert(deleteError);
dispatch(clearErrors());
}
if (isDeleted) {
alert("Product Deleted Successfully");
navigate("/admin/dashboard");
dispatch({ type: DELETE_PRODUCT_RESET });
}
dispatch(getAdminProduct());
}, [dispatch,error,deleteError,isDeleted]);
return (
<Fragment>
<div className='adminproductlistedit'>
<div className='table_overflow'>
<table id='table'>
<thead> {/* The Head of The Table */}
<tr>
<th className='editTable editProductId'>Product ID</th>
<th className='editTable editName'>Name</th>
<th className='editTable editStock'>Stock</th>
<th className='editTable editCategory'>Category</th>
<th className='editTable editPrice'>Price</th>
<th className='editTable adminActions'>Actions</th>
</tr>
</thead>{/* End of The Head */}
<tbody> {/* The Body of The Table */}
{products &&
products.map((item) => (
<tr className='tr'>
<td>{item._id}</td>
<td>{item.name}</td>
<td>{item.stock}</td>
<td>{item.category}</td>
<td>{item.price}</td>
<td className='actiondiv'>
<button><Link to={`/admin/product/${item._id}`}>
Edit <i className=" editicon fa-solid fa-pencil" />
</Link></button>
<button onClick={() => {
dispatch(deleteProduct(item._id))}}
>Delete <i className="deleteicon fa-solid fa-trash-can" /></button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</Fragment>
)
}
export default ProductList
You should declare your DELETE route as a delete request:
router
.route('/admin/product/:id')
.put(isAuthenticatedUser, authorizeRoles('admin'), updateProduct)
.delete(isAuthenticatedUser, authorizeRoles('admin'), deleteProduct);
Also, you can simplify your controller by using findByIdAndDelete:
exports.deleteProduct = catchAsyncError(async(req,res,next) => {
await Product.findByIdAndDelete(req.params.id)
res.status(200).json({
success:true,
message:"product deleted successfully"
})
})
I am new to React js, and I am trying to create a contact list. Where you can add contacts, update, and delete contacts from your contact list. However, I am trying to initialize the state of my contact list but when I run it, it displays the contact list screen without the "initialState" of the contact list. Then when I try to add a contact it then throws a type error screen(state is not iterable). How do I fix this issue?
contactReducer.js:
const initialState = [
{ id: 0, name: "Raman Sharma", Level: " 20", Progress: "Master" },
{ id: 1, name: "Test Name", Level: " 25", Progress: "Master" },
];
export const contactReducer = (state = initialState , action) => {
switch (action.type) {
case "ADD_CONTACT":
state = [...state, action.payload];
return state;
case "DELETE_CONTACT":
const contactFilter = state.filter((contact) =>
contact.id === action.payload ? null : contact
);
state = contactFilter;
return state;
case "UPDATE_CONTACT":
const contactUpdate = state.filter((contact) =>
contact.id === action.payload.id
? Object.assign(contact, action.payload)
: contact
);
state = contactUpdate;
return state;
case "RESET_CONTACT":
state = [{ name: null, Level: null, Progress: null }];
return state;
default:
return state;
}
};
./client/AddContact/index.js
import React, { useState } from "react";
import { connect } from "react-redux";
import { useHistory } from "react-router";
import { toast } from "react-toastify";
const AddPost = ({ contacts, addContact }) => {
const [name, setName] = useState("");
// const [Level, setLevel] = useState("");
// const [Progress, setProgress] = useState("");
const history = useHistory();
const handleSubmit = (e) => {
e.preventDefault();
// const checkContactLevelExists = contacts.filter((contact) =>
// contact.Level === Level ? contact : null
//);
// const checkContactProgressExists = contacts.filter((contact) =>
// contact.Progress === Progress ? contact : null
//);
if ( !name) {
return toast.warning("Please fill in field!!");
}
// if (checkContactLevelExists.length > 0) {
// return toast.error("This Level already exists!!");
// }
// if (checkContactProgressExists.length > 0) {
// return toast.error("This Progress number already exists!!");
// }
const data = {
id: contacts.length > 0 ? contacts[contacts.length - 1].id + 1 : 0,
// Level,
name,
// Progress,
};
addContact(data);
toast.success("Player Added successfully!!");
history.push("/");
};
return (
<div className="container-fluid">
<h1 className="text-center text-dark py-3 display-2">Add Friend</h1>
<div className="row">
<div className="col-md-6 p-5 mx-auto shadow">
<form onSubmit={handleSubmit}>
<div className="form-group">
<input
className="form-control"
type="text"
placeholder="Full name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
{/* <div className="form-group">
<input
className="form-control"
type="Level"
placeholder="Level"
value={Level}
onChange={(e) => setLevel(e.target.value)}
/>
</div> */}
{/* <div className="form-group">
<input
className="form-control"
type="number"
placeholder="Progress"
value={Progress}
onChange={(e) => setProgress(e.target.value)}
/>
</div> */}
<div className="form-group">
<input
className="btn btn-block btn-dark"
type="submit"
value="Add Student"
/>
</div>
</form>
</div>
</div>
</div>
);
};
const mapStateToProps = (state) => ({
contacts: state,
});
const mapDispatchToProps = (dispatch) => ({
addContact: (data) => {
dispatch({ type: "ADD_CONTACT", payload: data });
},
});
export default connect(mapStateToProps, mapDispatchToProps)(AddPost);
home.js
import React from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
const Friends = ({ contacts, deleteContact }) => {
return (
<div className="container">
<div className="row d-flex flex-column">
<Link to="/add" className="btn btn-outline-dark my-5 ml-auto ">
Add Friend
</Link>
<div className="col-md-10 mx-auto my-4">
<table className="table table-hover">
<thead className="table-header bg-dark text-white">
<tr>
<th scope="col">Id</th>
<th scope="col">Name</th>
<th scope="col">Level</th>
<th scope="col">Progess</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{contacts.length > 0 ? (
contacts.map((contact, id) => (
<tr key={id}>
<td>{id + 1}</td>
<td>{contact.name}</td>
<td>{contact.Level}</td>
<td>{contact.Progress}</td>
<td>
<Link
to={`/edit/${contact.id}`}
className="btn btn-sm btn-primary mr-1"
>
Edit
</Link>
<button
type="button"
onClick={() => deleteContact(contact.id)}
className="btn btn-sm btn-danger"
>
Remove
</button>
</td>
</tr>
))
) : (
<tr>
<th>No contacts found</th>
</tr>
)}
</tbody>
</table>
</div>
</div>
</div>
);
};
const mapStateToProps = (state) => ({
contacts: state,
});
const mapDispatchToProps = (dispatch) => ({
deleteContact: (id) => {
dispatch({ type: "DELETE_CONTACT", payload: id });
},
});
export default connect(mapStateToProps, mapDispatchToProps)(Friends);
The initial state's an object with a contactReducer property that is the array of contacts.
I'm certain you've just specified incorrect default state value.
Instead of
state = { contactReducer: initialState }
You likely want
state = initialState
All the reducers will now correctly specify/update state as an array.
Since the contactReducer reducer was combined with another reducer and provided to the store, this nests the state slice under the key you combined them with.
const reducer = combineReducers({
login: authReducer,
contactInfo: contactReducer, // <-- this
});
Instead of accessing via just state.X it is now state.contactInfo.X.
const mapStateToProps = (state) => ({
contacts: state.contactInfo,
});
However, there might be an issue in delete contact case in the use of filter.
Callback passed to a filter should return true or false only based on which it will be included or not in resultant array, instead you seem to have returned null or object.
Corrected:
case "DELETE_CONTACT":
const contactFilter = state.filter((contact) =>
contact.id !== action.payload
);
state = contactFilter;
return state;
The above filter will return true if the id's are not equal, which means it will be considered or false which means it will not be included in the resultant array
Additionally, in case of Update, you need to replace filter with map function as below:
case "UPDATE_CONTACT":
const contactUpdate = state.map((contact) =>
contact.id === action.payload.id
? {...contact, ...action.payload}
: contact
);
state = contactUpdate;
return state;
Furthermore, i would changes the contactReducer.js as below:
const initialState = { contacts: [
{ id: 0, name: "Raman Sharma", Level: " 20", Progress: "Master" },
{ id: 1, name: "Test Name", Level: " 25", Progress: "Master" },
]};
export const contactReducer = (state = initialState , action) => {
switch (action.type) {
case "ADD_CONTACT":
return {contacts: [...state.contacts, action.payload]};
case "DELETE_CONTACT":
return {contacts: state.contacts.filter((contact) =>
contact.id !== action.payload
)};
case "UPDATE_CONTACT":
return {contacts: state.contacts.map((contact) =>
contact.id === action.payload.id
? {...contact, ...action.payload}
: contact
)};
case "RESET_CONTACT":
return {contacts: [{ name: null, Level: null, Progress: null }]};
default:
return state;
}
};
Goal:
Value 1 show css > badge-primary
Value 2 show css > badge-secondary
The rest of the value show css > badge-danger
It take place at function handleChange.
Problem:
I was only enable to use false or true in order to use css. It is only two option.
The third option should be available and how should it be solved? Any suggestoin?
Info:
*Newbie in reactjs
Stackblitz:
https://stackblitz.com/edit/react-34tdvs?
css
h1,
p {
font-family: Lato;
}
.badge-primary {
text-align: center;
color: blue;
}
.badge-danger {
font-size: 15px;
color: red;
}
.badge-secondary {
font-size: 15px;
color: rgb(0, 204, 255);
}
import React from 'react';
import './style.css';
import React, { Component } from 'react';
import axios from 'axios';
export default class App extends Component {
constructor() {
super();
this.state = {
isLoaded: false,
listData: [],
list2Data: [],
list3DataRaw: [],
list3Data: [],
value: true
};
}
componentDidMount() {
axios
.get('https://jsonplaceholder.typicode.com/comments?postId=1')
.then(response =>
this.setState({
isLoaded: true,
listData: response.data
})
);
axios.get('https://jsonplaceholder.typicode.com/users').then(response =>
this.setState({
isLoaded: true,
list2Data: response.data
})
);
axios.get('https://jsonplaceholder.typicode.com/todos').then(response =>
this.setState({
isLoaded: true,
list3DataRaw: response.data
})
);
}
handleChange = ({ target }) => {
// copy current list of Item
const list = [...this.state.list3DataRaw];
if (1 == target.value) {
this.setState({
value: false
});
}
if (1 != target.value) {
this.setState({
value: true
});
}
// filter out item being deleted
const updateList = list.filter(item => item.userId == target.value);
this.setState({
list3Data: updateList
});
};
render() {
const { isLoaded } = this.state;
const locations =
this.state.list2Data &&
this.state.list2Data.map(location => {
return { value: location.id, label: location.name };
});
const locationss =
this.state.list3Data &&
this.state.list3Data.map(location => {
return { value: location.userId, label: location.title };
});
//console.log(locations);
//console.log(locationss);
if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<>
<select
id="selectLocation"
value={locations.value}
onChange={this.handleChange}
>
{locations.map(({ value, label }, index) => (
<option value={value}>{label}</option>
))}
</select>
<select
id="selectLocationn"
className={this.state.value ? 'badge-primary' : 'badge-danger'}
value={locationss.value}
onChange={this.handleChange}
>
{locationss.map(({ value, label }, index) => (
<option value={value}>{label}</option>
))}
</select>
<table className="table">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{this.state.listData &&
this.state.listData.map(item => {
return (
<tr key={item.id.toString()}>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.email}</td>
</tr>
);
})}
</tbody>
</table>
</>
);
}
}
}
Here is the working branch,
https://stackblitz.com/edit/react-dwtfr1
I will add comments in below code so you will know what I am doing.
import React from 'react';
import './style.css';
import React, { Component } from 'react';
import axios from 'axios';
export default class App extends Component {
constructor() {
super();
// Modified state a bit to remove getting the raw data. We can get what we need when we fetch data.
this.state = {
isLoaded: false,
listData: [],
list2Data: [],
//Selected items needs to go in another state. I am selecting first item by default.
selectedlist2DataItem: 1,
selectedlist3DataItem: 1,
list3Data: []
};
}
componentDidMount() {
axios
.get('https://jsonplaceholder.typicode.com/comments?postId=1')
.then(response => {
//You set state logic was wrong. You have to bring only state and update what's new. ...this.state, is for that.
this.setState({
...this.state,
isLoaded: true,
listData: response.data
});
});
axios.get('https://jsonplaceholder.typicode.com/users').then(response =>
//Set set state issue as above one
this.setState({
...this.state,
isLoaded: true,
list2Data: response.data.map(location => {
return { value: location.id, label: location.name };
})
})
);
axios.get('https://jsonplaceholder.typicode.com/todos').then(response =>
//Set set state issue as above one
this.setState({
...this.state,
isLoaded: true,
// Moved the logic here
list3Data: response.data.map(location => {
return { value: location.userId, label: location.title };
})
})
);
}
// We are handing 2 dropdown events so we need to know which is what. That's why I added name.
handleChange = ({ target }, name) => {
if (name === 'selectLocation') {
// Selected item we need a number that's why +target.value. + makes a string to number.
this.setState({
...this.state,
selectedlist2DataItem: +target.value
});
} else {
this.setState({
...this.state,
selectedlist3DataItem: +target.value
});
}
};
render() {
const { isLoaded } = this.state;
if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<>
<select
id="selectLocation"
value={this.state.selectedlist2DataItem}
onChange={e => this.handleChange(e, 'selectLocation')}
>
{/* maping is done on state value. value property is also using state. */}
{this.state.list2Data.map(({ value, label }, index) => (
<option value={value}>{label}</option>
))}
</select>
{/* Your class logic with values. use State for this pupose */}
<select
id="selectLocationn"
className={
this.state.selectedlist3DataItem === 1
? 'badge-primary'
: this.state.selectedlist3DataItem === 2
? 'badge-secondary'
: 'badge-danger'
}
value={this.state.selectedlist3DataItem}
onChange={e => this.handleChange(e, 'selectLocationn')}
>
{/* maping is done on state value.value property is also using state. */}
{this.state.list3Data.map(({ value, label }, index) => (
<option value={value}>{label}</option>
))}
</select>
<table className="table">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{this.state.listData &&
this.state.listData.map(item => {
return (
<tr key={item.id.toString()}>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.email}</td>
</tr>
);
})}
</tbody>
</table>
</>
);
}
}
}
Result
...........Another solution:
https://stackblitz.com/edit/react-8aqbpa?
I am new to react, I have an array of users in my redux store, these users came from my backend using the axios fetch api, I noticed that I couldn't map through the empty array which I declared because the user where all nested in another array so my resulting map method was undefined.
MY REDUCER
import {
GET_STATE,
GET_USERS,
ADD_USER,
DELETE_USER,
ADD_COUNT,
SET_LOADING,
SET_ERROR, } from './actionTypes'
const initState = {
users: [],
loading: false,
error: ''
}
const userReducer = (state = initState, action)=> {
switch(action.type) {
case GET_STATE:
return {
...state,
...state.users
}
case ADD_USER:
return {
...state,
users: [action.payload, ...state.users,]
}
case DELETE_USER:
return {
...state,
users: state.users.filter( user => user.id !== action.payload)
}
case ADD_COUNT:
return {
...state,
count: state.count + 1
}
case SET_LOADING:
return {
...state,
loading: true
}
case SET_ERROR:
return {
...state,
error: action.payload
}
default:
return state
}
}
export default userReducer
USER LIST COMPONENT
import { connect } from 'react-redux'
import React, { useEffect } from 'react'
import { getUsers, deleteUser } from '../redux/actions'
function UsersList({users, id, getUsers, deleteUser}) {
const myStyle = {
display: 'block',
padding: '10px 0',
margintop: '5px',
borderBottom: 'solid 2px grey'
}
const btnstyle = {
cursor: 'pointer',
}
const tableData = {
height: '50px',
padding: '5px',
margin: '2px 0',
background: '#2c2b2c',
padding: '2px 5px',
'boxSizing': 'borderBox',
textTransform: 'uppercase'
}
const center = {
marginLeft: '20px'
}
useEffect(()=> {
getUsers()
console.log('======useEffect is being Called! to fetch List of Users...=========')
}, [])
const handleDelete = (id)=> {
deleteUser(id)
}
const userTable = users.map( user => {
// the close Fix i could get was to use "user.[0].lastName" i could access a property
// but it wasnt the best idea
console.log('From UserList component===.', user.lastName)
console.log('From UserList component===.', user)
return (
<table>
<thead>
<tr>
<td>ID</td>
<td>first name</td>
<td>last name</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<div style={tableData}>
<button onClick={()=> handleDelete(user._id)}>{user._id}-Del</button>
</div>
</td>
<td>
<div style={tableData}>{user.name} </div>
</td>
<td>
<div style={tableData}> {user.lastName} </div>
</td>
</tr>
</tbody>
</table>
)
})
return (
<div className="side-bar">
<div className="side-bar-heading">User Lists</div>
{
users.length ? userTable : <h3 style={center}>There are no user registered!</h3>
}
</div>
)
}
const mapStateToProps = (state)=> {
return {
users: state.users,
loading: state.loading
}
}
const mapDispatchToProps = (dispatch)=> {
return {
getUsers: ()=> dispatch(getUsers()),
deleteUser: (id)=> dispatch(deleteUser(id))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(UsersList)
App.js
import React, { Component } from "react";
import "./App.css";
import Router from "./Router";
class App extends Component {
render() {
return (
<div className="App">
<div>
<h1>React-Redux Store</h1>
<h2>Welcome to the React Store</h2>
</div>
<Router />
</div>
);
}
}
export default App;
index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import "bootstrap/dist/css/bootstrap.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducer from "./reducer";
import "../node_modules/font-awesome/css/font-awesome.min.css";
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
ShopHome.js
when i open the page, it appears TypeError: Cannot read property 'items' of undefined, I guess its something wrong with mapStateToProps and cannot define the state. i Wonder if i did something wrong in the reducer
import React, { Component } from "react";
import { NavLink } from "react-router-dom";
import { connect } from "react-redux";
import { addToCart } from "./action_type";
class ShopHome extends Component {
handleClick = id => {
this.props.addToCart(id);
};
render() {
return (
<div>
<table className="table">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Price</th>
<th>
<NavLink to="/myCart" exact activeStyle={{ color: "green" }}>
my cart
</NavLink>
</th>
</tr>
</thead>
<tbody>
{this.props.items.map(item => {
return (
<tr key={item.id}>
<td>{item.name}</td>
<td>{item.description}</td>
<td>${item.price}</td>
<button to="/" onClick={() => this.handleClick(item.id)}>
add to cart
</button>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
}
const mapStateToProps = state => {
return {
items: state.items
};
};
const mapDispatchToProps = dispatch => {
return {
addToCart: id => {
dispatch(addToCart(id));
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ShopHome);
ShopCart.js
when i add the add quantity functionality it all works fine, but however after i added subtract quantity functionality it turns out says state is not defined(
TypeError: Cannot read property 'items' of undefined
Function.mapStateToProps [as mapToProps]
src/shopHome.js:47
44 | }
45 | const mapStateToProps = state => {
46 | return {
> 47 | items: state.items
48 | };
49 | };
50 | )
ShopCart.js
import React, { Component } from "react";
import { NavLink } from "react-router-dom";
import { connect } from "react-redux";
import { addQuantity } from "./action_type";
import { subtractQuantity } from "./action_type";
class ShopCart extends Component {
handleAddQuantity = id => {
this.props.addQuantity(id);
};
handleSubtractQuantity = id => {
this.props.subtractQuantity(id);
};
render() {
let addedItems = this.props.items.map(item => {
return (
<tr key={item.id}>
<td>{item.name}</td>
<td>
<NavLink to="/myCart">
<span>
<i
className="fas fa-plus-circle"
onClick={() => {
this.handleAddQuantity(item.id);
}}
></i>
</span>
</NavLink>
{item.quantity}
<NavLink to="/myCart">
<span>
<i
className="fas fa-minus-circle"
onClick={() => {
this.handleSubtractQuantity(item.id);
}}
></i>
</span>
</NavLink>
</td>
<td>${item.price}</td>
</tr>
);
});
return (
<div>
<table className="table">
<thead>
<tr>
<th>Item</th>
<th>Quantity</th>
<th>Price</th>
<th>
<NavLink to="/" exact activeStyle={{ color: "green" }}>
back to store
</NavLink>
</th>
</tr>
</thead>
<tbody>{addedItems}</tbody>
</table>
</div>
);
}
}
const mapStateToProps = state => {
return {
items: state.addedItems
};
};
const mapDispatchToProps = dispatch => {
return {
addQuantity: id => dispatch(addQuantity(id)),
subtractQuantity: id => dispatch(subtractQuantity(id))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ShopCart);
reducer.js
import { ADD_TO_CART, ADD_QUANTITY, SUBTRACT_QUANTITY } from "./action.js";
const initialState = {
items: [
{
id: 1,
name: "apple",
description: "Eat One Every Day, may keep the doctor away",
price: 12
},
{
id: 2,
name: "grape",
description: "Wine is great, but grapes is better",
price: 11
},
{
id: 3,
name: "pineapple",
description: "enjoy but don`t forget to peer first",
price: 8
}
],
addedItems: []
};
const reducer = (state = initialState, action) => {
if (action.type === ADD_TO_CART) {
let addedItem = state.items.find(item => item.id === action.id);
let existed_item = state.addedItems.find(item => item.id === action.id);
if (existed_item) {
addedItem.quantity += 1;
return {
...state
};
} else {
addedItem.quantity = 1;
return {
...state,
addedItems: [...state.addedItems, addedItem]
};
}
}
if (action.type === ADD_QUANTITY) {
let addedItem = state.items.find(item => item.id === action.id);
addedItem.quantity += 1;
return {
...state
};
}
if (action.type === SUBTRACT_QUANTITY) {
let addedItem = state.items.find(item => item.id === action.id);
if (addedItem.quantity === 1) {
let newItem = state.addedItems.filter(item => item.id !== action.id);
return {
...state,
addedItems: newItem
};
} else {
addedItem.quantity -= 1;
return {
...state
};
}
}
};
export default reducer;
action_type.js
import { ADD_TO_CART, ADD_QUANTITY, SUBTRACT_QUANTITY } from "./action";
export const addToCart = id => {
return {
type: ADD_TO_CART,
id
};
};
export const addQuantity = id => {
return {
type: ADD_QUANTITY,
id
};
};
export const subtractQuantity = id => {
return {
type: SUBTRACT_QUANTITY,
id
};
};
action.js
export const ADD_TO_CART = "ADD_TO_CART";
export const ADD_QUANTITY = "ADD_QUANTITY";
export const SUBTRACT_QUANTITY = "SUBTRACT_QUANTITY";
Hi everyone, I am new to react-redux, when i open the page, the page keep telling me that TypeError: Cannot read property 'items' of undefined in the ShopHome.js, i supppose its something wrong with declaring the state in the mapStateToProps function. can someone give me a hand?