Reactjs: How to recognized clicked list - javascript

I'm having trouble with my list of buttons. What I'm trying to achieve is simple: click button a, show list of a1 to a3, click button b, show list of b1 to b3. I'm trying to make react to recognize which button is clicked. So far i ended up with nothing, everytime i click a button button a and b will display its children
these are my codes
CategoryContainer
import React, {Component} from 'react'
import {connect} from 'react-redux'
import {getCategory} from '../actions/categoryActions'
import {ShowSubCategory} from '../actions/interactionActions'
import {ShowItemCategory} from '../actions/interactionActions'
import Category from '../components/category'
class CategoryContainer extends Component{
constructor(props){
super(props);
console.log('category props', this.props.categories);
this.state = {
showsubcategory: false,
showitemcategory: false,
selectedsubcat: null,
selecteditemcat: null
}
}
componentWillMount(){
console.log('masuk CDM');
this.props.fetchCategory()
}
toggleSubCategory = (event) => {
console.log('masuk togglesubcategory', event);
this.setState({showsubcategory: !this.state.showsubcategory}, ()=>{
var isOpen = this.state.showsubcategory
this.props.showSubCategory(isOpen)
// if(isOpen){
// console.log('masuk block');
// document.getElementById("subcategorycontainer-" + id).style.display = "block"
//this does not work
// }
// else{
// console.log('masuk none');
// document.getElementById("subcategorycontainer-" + id).style.display = "none"
// }
})
}
render(){
var viewtypequery = window.innerWidth >= 1025 ? "computers" : "mobile"
return(
<Category alphabets={this.state.alph}
categorylist={this.props.categories}
view={viewtypequery}
isFetching={this.props.fetching}
togglesub={this.toggleSubCategory}
showsub={this.state.showsubcategory}
/>
)
}
}
const mapStateToProps = (state) => {
console.log('state mapstate', state);
return{
categories: state.category.categories,
fetching: state.category.isfetching,
error: state.category.error,
showsubcategory: state.interaction.showsubcategory,
showitemcategory: state.interaction.showitemcategory
}
}
const mapDispatchToProps = (dispatch) => {
return{
fetchCategory: () => {
dispatch(getCategory())
},
showSubCategory: (showsubcategory) => {
dispatch(ShowSubCategory(showsubcategory))
},
showItemCategory: (showitemcategory) => {
dispatch(ShowItemCategory(showitemcategory))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CategoryContainer)
category
import React from 'react'
import {Link} from 'react-router'
import Subcategory from './subcategory'
import BrandContainer from '../containers/brandContainer'
class Category extends React.Component{
constructor(props){
super(props)
}
render(){
console.log('isi ref cuk', this.subcatcontainer);
console.log('props category', this.props);
console.log('from category', this.props.categorylist);
if(this.props.fetching)
return(<div>loading..</div>)
// const showitem = this.props.categorylist.
const categorylist = this.props.categorylist.map((cl, index) => {
var subcategory = cl.children.map(sc => {
return <Subcategory key={sc.id} subcatname={sc.categoryname} subcatchild={sc.children} id={sc.id}/>
})
return(
<div>
<div class="listheader">
<button key={cl.id} id={"category_" + cl.id} class="category listheaderinner" onClick={this.props.togglesub.bind(this, index)}>
{cl.categoryname}
<span class="icon-caret kCaret kCaret-right icon-size-small"></span>
</button>
</div>
<div id={"subcategorycontainer-" + cl.id} class="subcategorycontainer container-fluid">
{subcategory}
</div>
</div>
)
}, this)
switch(this.props.view){
case "mobile":
return(
<div class="category-container">
<BrandContainer />
<div id="sale" class="listheader">
<button class="category listheaderinner">SALE<span class="icon-caret kCaret kCaret-right icon-size-small"></span></button>
</div>
{categorylist}
</div>
)
break;
}
}
}
export default Category
and i found a post on SO suggesting that i should breakdown the array into another component so here is my
subcategory
import React, {Component} from 'react'
import {connect} from 'react-redux'
class Subcategory extends Component{
render(){
console.log('showsub', this.props.showsubcategory);
var displayquery = this.props.showsubcategory ? "sublistitem listheaderinner ke-btn-transparent" : "sublistitem-hide listheaderinner ke-btn-transparent"
console.log(displayquery);
return(
<div>
<button key={this.props.id} id={"subcategory_" + this.props.id} class={displayquery}>
{this.props.subcatname}
<span class="icon-caret kCaret kCaret-right icon-size-small"></span>
</button>
<div class="itemcategorycontainer container-fluid">
</div>
</div>
// <li class="sublistitem categorylistitem" key={sc.id}><Link to="/category">{sc.categoryname}</Link></li>
)
}
}
const mapStateToProps = (state) => {
return{
showsubcategory: state.interaction.showsubcategory
}
}
export default connect(mapStateToProps)(Subcategory)
I really need help, I've been googling solutions even posted here for days and ended up with nothing

Related

Refactor class to function components in React

I'm having a hard time converting these 3 class components to function components, the class components are working i just am trying to convert them for learning purposes.
the API call: yelp.js
const { default: SearchBar } = require("../components/SearchBar/SearchBar");
const Yelp = {
searchYelp(term, location) {
return fetch(`/api/hello?term=${term}&location=${location}`)
.then((response) => {
// console.log(response)
return response.json()
}).then((jsonResponse) => {
// console.log(jsonResponse)
if (jsonResponse.businesses) {
return jsonResponse.businesses.map((business) => {
return {
id: business.id,
imageSrc: business.image_url,
name: business.name,
address: business.location.address1,
city: business.location.city,
state: business.location.state,
zipCode: business.location.zip_code,
category: business.categories.title,
rating: business.rating,
reviewCount: business.review_count,
}
})
}
})
}
}
export default Yelp
The Home component as a function that renders a SearchBar and BusinessList component: Home.js
import React, { useState } from "react";
import BusinessList from '../../../src/components/BusinessList/BusinessList';
import SearchBar from '../../../src/components/SearchBar/SearchBar';
import Yelp from '../../util/yelp';
const Home = (term, location) => {
const [businesses, setBusinesses] = useState([]);
const searchYelp = Yelp.searchYelp(term, location).then(businesses => {
setBusinesses(businesses)
})
return (
<>
<SearchBar searchYelp={searchYelp} />
<BusinessList business={businesses} />
</>
)
}
export default Home;
The Home component as a class: Home.js
// import React from 'react';
// import BusinessList from '../../../src/components/BusinessList/BusinessList';
// import SearchBar from '../../../src/components/SearchBar/SearchBar';
// import Yelp from '../../util/yelp';
// class Home extends React.Component {
// constructor() {
// super();
// this.state = {
// businesses: [],
// };
// this.searchYelp = this.searchYelp.bind(this);
// }
// searchYelp(term, location, sortBy) {
// Yelp.searchYelp(term, location, sortBy).then((businesses) => {
// this.setState({ businesses: businesses })
// })
// }
// render() {
// return (
// <>
// <SearchBar searchYelp={this.searchYelp} />
// <BusinessList businesses={this.state.businesses} />
// </>
// )
// }
// }
// export default Home;
The BusinessList component as a function that renders a Business component: BusinessList.js
import React, { useState } from "react";
import './BusinessList.css';
import Business from '../Business/Business';
function BusinessList(businesses) {
console.log(businesses)
return (
<div className="BusinessList">
{
businesses.map(business => {
<Business key={business.id} business={business} />
})
}
</div>
)
};
export default BusinessList;
The BusinessList component as a class: BusinessList.js
// import React from 'react';
// import './BusinessList.css';
// import Business from '../Business/Business';
// class BusinessList extends React.Component {
// constructor(props) {
// super(props)
// console.log(props.businesses)
// }
// render() {
// return (
// <div className="BusinessList">
// {
// this.props.businesses.map((business) => {
// return <Business key={business.id} business={business} />
// })
// }
// </div>
// )
// }
// };
// export default BusinessList;
The Business component as a function: Business.js
import React from "react";
import './Business.css';
const Business = (business) => {
return (
<div className="Business">
<div className="image-container">
<img src={business.business.imageSrc} alt={business.imageSrc} />
</div>
<h2>{business.business.name}</h2>
<div className="Business-information">
<div className="Business-address">
<p>{business.business.address}</p>
<p>{business.business.city} {business.state} {business.zipCode}</p>
</div>
<div className="Business-reviews">
<h3>{business.business.category}</h3>
<h3 className="rating">{business.business.rating}</h3>
<p>{business.business.reviewCount} reviews</p>
</div>
</div>
</div>
)
};
export default Business;
The Business component as a class: Business.js
// import React from "react";
// import './Business.css';
// class Business extends React.Component {
// render() {
// const { business } = this.props
// return (
// <div className="Business">
// <div className="image-container">
// <img src={business.imageSrc} alt={business.imageSrc} />
// </div>
// <h2>{business.name}</h2>
// <div className="Business-information">
// <div className="Business-address">
// <p>{business.address}</p>
// <p>{business.city} {business.state} {business.zipCode}</p>
// </div>
// <div className="Business-reviews">
// <h3>{business.category}</h3>
// <h3 className="rating">{business.rating}</h3>
// <p>{business.reviewCount} reviews</p>
// </div>
// </div>
// </div>
// )
// }
// };
// export default Business;
EDIT **
My attempt at SearchBar component as function: SearchBar.js
import React, { useState, useEffect } from "react";
import './SearchBar.css';
const SearchBar = (props) => {
const [term, setTerm] = useState('')
const [location, setLocation] = useState('')
const [sortBy, setSortBy] = useState('best_match')
const sortByOptions = {
'Best Match': 'best_match',
'Highest Rated': 'rating',
'Most Reviewed': 'review_count'
};
const handleSortByChange = () => {
setSortBy(sortBy)
// console.log(sortByOption)
console.log(sortBy)
}
const renderSortByOptions = (sortByOptions) => {
// console.log(Object.keys(sortByOptions))
return Object.keys(sortByOptions).map(sortByOption => {
let sortByOptionValue = sortByOptions[sortByOption]
// console.log(sortByOptionValue)
return <li
className={sortBy === sortByOption ? 'active' : ''}
onClick={handleSortByChange}
key={sortByOptionValue}>
{sortByOption}
</li>;
})
}
const handleTermChange = (event) => {
setTerm(event.target.value)
}
const handleLocationChange = (event) => {
setLocation(event.target.value)
}
const handleSearch = (event) => {
event.preventDefault()
props.searchYelp(term, location)
}
return (
<div className="SearchBar">
{props.searchYelp}
<div className="SearchBar-sort-options">
<ul>
{renderSortByOptions(sortByOptions)}
</ul>
</div>
<div className="SearchBar-fields">
<input
onChange={handleTermChange}
placeholder="Search Businesses"
/>
<input
onChange={handleLocationChange}
placeholder="Where?"
/>
<button className="SearchBar-submit" onClick={handleSearch}>Let's Go</button>
</div>
</div>
)
}
export default SearchBar;
EDIT**
SearchBar component as a class: SearchBar.js
import React from 'react';
import './SearchBar.css';
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.state = {
term: '',
location: '',
sortBy: 'best_match'
}
this.handleTermChange = this.handleTermChange.bind(this)
this.handleLocationChange = this.handleLocationChange.bind(this)
this.handleSearch = this.handleSearch.bind(this)
this.sortByOptions = {
'Best Match': 'best_match',
'Highest Rated': 'rating',
'Most Reviewed': 'review_count'
};
}
getSortByClass(sortByOption) {
// console.log(sortByOption)
if (this.state.sortBy === sortByOption) {
return 'active'
}
return ''
}
handleSortByChange(sortByOption) {
this.setState({
sortBy: sortByOption
})
}
handleTermChange(event) {
this.setState({
term: event.target.value
})
}
handleLocationChange(event) {
this.setState({
location: event.target.value
})
}
handleSearch(event) {
this.props.searchYelp(this.state.term, this.state.location, this.state.sortBy)
event.preventDefault()
}
renderSortByOptions() {
return Object.keys(this.sortByOptions).map(sortByOption => {
let sortByOptionValue = this.sortByOptions[sortByOption]
console.log(sortByOptionValue)
return <li
onClick={this.handleSortByChange.bind(this, sortByOptionValue)}
className={this.getSortByClass(sortByOptionValue)}
key={sortByOptionValue}>
{sortByOption}
</li>;
})
}
render() {
return (
<div className="SearchBar">
{this.searchYelp}
<div className="SearchBar-sort-options">
<ul>
{this.renderSortByOptions()}
</ul>
</div>
<div className="SearchBar-fields">
<input onChange={this.handleTermChange} placeholder="Search Businesses" />
<input onChange={this.handleLocationChange} placeholder="Where?" />
<button className="SearchBar-submit" onClick={this.handleSearch}>Let's Go</button>
</div>
</div>
)
}
};
export default SearchBar;
I keep getting the error "Cannot read properties of undefined (reading 'map')
Or the error "Businesses.map is not a function"
Im also a little confused as to why when everything is converted to function components in my final Business component in order to get things to showup im required to pass things in as business.business.imageSrc instead of just business.imageSrc
First in Home searchYelp should be declared as a function so it can be passed as a callback to the SearchBar component.
const Home = () => {
const [businesses, setBusinesses] = useState([]);
const searchYelp = (term, location) => {
Yelp.searchYelp(term, location)
.then(businesses => {
setBusinesses(businesses);
});
};
return (
<>
<SearchBar searchYelp={searchYelp} />
<BusinessList business={businesses} />
</>
)
};
Then in BusinessList you need to access the passed business prop. Your current code is naming the props object businesses and then attempts to map it. It could be businesses.business.map, but by convention we name the props object props or simply destructure the props you want to use. You need to also return the Business component you are mapping to.
function BusinessList({ business }) {
return (
<div className="BusinessList">
{business.map(business => {
return <Business key={business.id} business={business} />;
})}
</div>
)
};
Same issue with the props object name in the Business component.
const Business = (props) => {
return (
<div className="Business">
<div className="image-container">
<img src={props.business.imageSrc} alt={props.business.imageSrc} />
</div>
<h2>{props.business.name}</h2>
<div className="Business-information">
<div className="Business-address">
<p>{props.business.address}</p>
<p>{props.business.city} {props.business.state} {business.zipCode}</p>
</div>
<div className="Business-reviews">
<h3>{props.business.category}</h3>
<h3 className="rating">{props.business.rating}</h3>
<p>{props.business.reviewCount} reviews</p>
</div>
</div>
</div>
)
};
BusinessList receives props, an object containing the props passed in.
The function parameter would either need to destructure it:
function BusinessList({ businesses }) { ... }
Or reference it off the props object:
function BusinessList(props) {
console.log(props.businesses)
// ...
}
Few notes:
Right now Yelp.searchYelp returns Promise<any[] | undefined>, i.e undefined is a legitimate value that the consume may get. Up to you to decide if setBusinesses(businesses) when businesses is undefined is useful or not, but in that case, handle it. Otherwise default to an empty array, setBusinesses(businesses ?? []) or throw an error.
Do not run side effects in the render phase, i.e call the api inside a useEffect:
React.useEffect(() => {
const searchYelp = Yelp.searchYelp(term, location).then(businesses => {
setBusinesses(businesses ?? [])
})
}, [term, location])
Lastly, const Business = (business) => { here business is actually the props object. You can simply destructure it const Business = ({ business }) => { to get the value directly.

Attempting to place data from an API onto a modal in React

I'm attempting to put data that I'm getting from an API onto a modal that will appear whenever a button is clicked.
How is this done? I'm able to use the data from the API without the modal, so I know it's not an issue with the syntax of my componentDidMount(). Not sure what the issue is and how it can be resolved.
import React from 'react';
import './App.css';
import Nav from './Nav';
import Meal from './Meal';
import meals from './Meals';
import Modal1 from './Modal'
function App() {
const mealArr = meals.map(item => <Meal food={item.food} picture={item.picture} type={item.id} />)
return (
<div className="content">
<Nav />
{mealArr}
<Modal1 isOpen={false}/>
</div>
);
}
export default App;
import React from 'react';
import Modal from 'react-modal';
class Modal1 extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false
}
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
items: json
})
})
}
render() {
const allItems = this.state.items;
let itemArr = allItems.map(item =>
<div>
<ul>
<li key={item.id}>{item.name}</li>
</ul>
</div>)
return (
<div>
<Modal>
{itemArr}
</Modal>
</div>
)
}
}
export default Modal1;
import React, {Component} from 'react';
import Modal1 from 'react-modal';
class Meal extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
}
this.handleClick = this.handleClick.bind(this);
this.turnOff = this.turnOff.bind(this);
}
handleClick() {
this.setState({isOpen: true})
}
turnOff() {
this.setState({isOpen: false})
}
render() {
return (
<div className="meal-container">
<h2>{this.props.type}</h2>
<h1>{this.props.food}</h1>
<img alt="" src={this.props.picture} />
<p className="steps-button" onClick={this.handleClick}>Steps</p>
<Modal1 className="modal-1" isOpen={this.state.isOpen}/>
</div>
)
}
}
export default Meal;
take a look at allItems, it's an empty array before you get the data from the API.
So, for the first render (before component did mount):
const allItems = this.state.items // ----> const allItems = []
mapping through an empty array will not produce any error and return another empty array, but when you map through an empty array, don't expect to have any item or item.name. so the itemArr is not as your expectation and cause the issue with rendering it.
to avoid from this issue, check your allItems to ensure that the data has arrived.
const allItems = this.state.items;
let itemArr = []
if (allItems.length > 0) {
itemArr = allItems.map(item => (
<div>
<ul>
<li key={item.id}>{item.name}</li>
</ul>
</div>
)
}
return (
<div>
<Modal>
{itemArr}
</Modal>
</div>
)

How to get photo ID in ReactJS App from JSONPlaceholder for Modal Window to see details of photo?

I have a problem with ReactJS Photobrowser App which is I can't get photo ID for Modal Window to see clicked photo from the thumbnail list of the photos.
Here is the PhotoLoader.js React Component source code below.
import React, { Component } from 'react';
import Modal from './Modal.js';
import Axios from 'axios';
import './PhotoLoader.css';
import Image from 'react-bootstrap/Image';
import Container from 'react-bootstrap/Container';
import Button from 'react-bootstrap/Button';
class PhotoLoader extends Component {
constructor(props) {
super(props);
this.state = {
photosPaginated: [], // will hold the results from our ajax call
show: false
}
this.showModal = this.showModal.bind(this);
this.hideModal = this.hideModal.bind(this);
this.loadPhotosPaginated();
}
loadPhotosPaginated = () => {
Axios.get('https://jsonplaceholder.typicode.com/photos?_start=0&_limit=40')
.then(response => {
const photosPaginated = response.data;
this.setState({ photosPaginated });
});
};
showModal = () => {
this.setState({ show: true });
};
hideModal = () => {
this.setState({ show: false });
};
render() {
return (
<Container className="PhotoLoader-photobrowser-grid-container">
<Modal show={this.state.show} handleClose={this.hideModal}></Modal>
{ this.state.photosPaginated.map(photo => <Button key={photo.id+"_button"} className="PhotoLoader-photo-button" onClick={this.showModal}><Image key={photo.id+"_photo"} src={photo.thumbnailUrl} className="PhotoLoader-thumbnail" thumbnail></Image></Button>)}
</Container>
);
};
}
export default PhotoLoader;
And here is Modal.js source code file below.
import React from 'react';
import './modal.css';
import Button from 'react-bootstrap/Button';
import Axios from 'axios';
import Image from 'react-bootstrap/Image';
var title;
var url;
const loadPhoto = (id) => {
Axios.get('https://jsonplaceholder.typicode.com/photos/'+id)
.then(response => {
title = response.data.title;
url = response.data.url;
});
return (
<div>
<p>{title}</p>
<Image src={url} className="PhotoLoader-large" fluid />
</div>
);
};
const Modal = ({ handleClose, show, children }) => {
const showHideClassName = show ? "modal display-block" : "modal display-none";
return (
<div className={showHideClassName}>
<section className="modal-main">
{children}
{loadPhoto(1)} //on this function I need to get correct ID from clicked photo on PhotoLoader.js React Component and not just number 1
<div className="clear"></div>
<Button onClick={handleClose}>
Close
</Button>
</section>
</div>
);
};
export default Modal;
Okay what is my problem is?
On Modal.js has a line {loadPhoto(1)} where number 1 is just ID for needed photo that be must to get dynamically from PhotoLoader.js file some how. How to do this?
This can be handled by passing the id to show model method as follows
showModal = (id) => {
this.setState({ show: true, id: id });
};
and in the model page you can use this value.
Code Sandbox here

not able to add items to cart from product page using react redux

I have listed out some products in product list page. I am not able to add product in my cart. It is showing error in my reducer code. Here is my code.
action
export const addToCart= id =>({
type: ADD_TO_CART,
id
});
reducer code
const initialState = {
products: [{id:1,title:'Dell Inspiron 3581', discount: 10, price:110,img:Image1},
{id:2,title:'Dell Vostro 3362', discount: 10, price:80,img: Image2},
{id:3,title:'Acer TravelMate', discount: 0,price:120,img: Image3},
{id:4,title:'Lenovo ideapad core', discount: 15, price:260,img:Image4},
{id:5,title:'Lenovo V145', discount: 0, price:160,img: Image5},
{id:6,title:'Asus Zephyrus G14', discount: 5 ,price:90,img: Image6}],
filteredProducts:[],
cartItems:[],
total:0,
cartCount: 0
};
export default function productReducer(state = initialState, action){
switch(action.type){
case ADD_TO_CART:
const { cartItems, products, cartCount,total } = state;
let productToAdd = cartItems.find(item=>item.id===action.id);
let count;
if (!productToAdd) {
productToAdd = products.find(obj => obj.id === action.id);
count = 1;
} else {
count = productToAdd.count + 1;
}
const pid = productToAdd.id;
const newCart = { ...state.cartItems, [pid]: { ...productToAdd, count } };
const newcount = cartCount + 1;
const newTotal = total+productToAdd.price
return { ...state, cartItems: newCart, cartCount: newcount,total:newTotal};
default:
return state;
}
}
here i am doing the count of added items if that product is already exist in cart then increase quantity of that product. But i am getting type error because of null reference.
TypeError: Unable to get property 'id' of undefined or null reference ->(const pid = productToAdd.id)
Here is my productcomponent page :
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import {addToCart} from '../../action/cartAction';
import {Button} from 'react-bootstrap';
class ProductComponent extends Component{
constructor(props){
super(props);
this.state ={
radioSearch : '',
filteredData: this.props.products
}
}
handleClick = (id)=>{
this.props.addToCart(id);
}
/** some other handler **/
render(){
let itemList = this.state.filteredData.map(item=>{
return(
<div className="card" key={item.id}>
<div className="card-image">
<img src={item.img} alt={item.title}/>
<span className="card-title">{item.title}</span>
</div>
<div className="card-content">
<p><b>Discount: {item.discount}%</b></p>
<p><b>Price: {item.price}$</b></p> <Button variant="dark" onClick={()=>{this.handleClick(item.id)}}>ADD TO CART</Button>
</div>
</div>
)
})
return(
<div className="container">
/** some other code **/
<div className="box">
{itemList}
</div>
</div>
)
}
}
const mapStateToProps = (state)=>{
return {
products: state.productReducer.products
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
addToCart
}, dispatch);
export default connect(mapStateToProps,mapDispatchToProps)(ProductComponent)
here is my cartcomponent
import React, { Component } from 'react';
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import {removeFromCart} from '../../action/cartAction';
class cartComponent extends Component {
handleRemove(id){
this.props.removeFromCart(id);
}
render() {
console.log(this.props.cartItems.length);
let cartProducts = this.props.cartItems.length ?
(
this.props.cart.map(item=>{
return(
<li className="collection-item avatar" key={item.id}>
<div className="item-img">
<img src={item.img} alt={item.img} className=""/>
</div>
<div className="item-desc">
<span className="title">{item.title}</span>
<p>{item.desc}</p>
<p><b>Price: {item.price}$</b></p>
<p>
<b>Quantity: {item.quantity}</b>
</p>
<button className="waves-effect waves-light btn pink remove" onClick={()=>{this.handleRemove(item.id)}}>Remove</button>
</div>
</li>
)
})
):
(
<p>Your cart is empty</p>
)
return(
<div className="container">
<div className="cart">
<h5>You have ordered:</h5>
<ul className="collection">
{cartProducts}
</ul>
</div>
</div>
)
}
}
function mapStateToProps(state) {
return {
cartItems : state.productReducer.cartItems
}
}
function mapActionsToProps(dispatch) {
return bindActionCreators({
removeFromCart
}, dispatch);
}
export default connect(mapStateToProps,mapActionsToProps)(cartComponent);
i have tried many things but it is not adding product to cart. what are the changes needed to make it work?
Here is my source code :
source code
It looks like the way you map the action to props does not match the actual usage of the function.
You map it like this:
addToCart: (id)=>{dispatch(addToCart(id))}
The action just passes its parameter as payload meaning payload is equal to id:
export const addToCart= payload =>({
type: ADD_TO_CART,
payload
});
But you expect the payload to be an object like this:
const { id } = action.payload;
You need to make them match. Either pass the action an object:
addToCart: (id)=>{dispatch(addToCart({id}))}
Or use the payload as the id:
const id = action.payload;
The result of the code as is, is that id is undefined, meaning no productToAdd.
In productReducer file you should push the item into state.cartItems
if (!productToAdd) {
productToAdd = products.find(obj => obj.id === action.id);
count = 1;
ADD row-> state.cartItems.push(productToAdd);
}
In this approach, if you don't enter a specified quantity of the product, it will be automatically placed to the cart as one item.

ReactJS TypeError: Cannot read property 'eventEmitter' of undefined

I'm currently building a ReactJS Weather app where I have a drop-down list with different cities and a container with the information about weather on the selected city. When i fetch the weather data from an API i have a default city and I want to refetch the data when user selects another city in the dropdown list.
I will provide you with the code.
App.jsx class (the main class)
import React, { Component } from "react";
import "./sass/app.scss";
import axios from "axios";
import { Dropdown } from "semantic-ui-react";
import NavigationBar from "./components/NavigationBar";
import WeatherComponent from "./components/WeatherComponent";
import { locationOptions } from "./locations.js";
const WEATHER_KEY = "5f0f0f2a61c0f3f650984fb442f03d86";
class App extends Component {
constructor(props) {
super(props);
this.state = {
cityName: "Pristina",
isLoading: true,
isSelectedLocationOpen: false
};
}
componentDidMount() {
const { cityName } = this.state;
const { eventEmitter } = this.props;
const URL = `http://api.weatherstack.com/current?access_key=${WEATHER_KEY}&query=${cityName}`;
axios
.get(URL)
.then(res => {
return res.data;
})
.then(data => {
this.setState({
isLoading: false,
name: data.location.name,
country: data.location.country,
temperature: data.current.temperature,
weather_descriptions: data.current.weather_descriptions[0],
weather_icons: data.current.weather_icons[0],
observation_time: data.current.observation_time
});
})
.catch(err => {
console.error("Cannot fetch weatcher from API", err);
});
eventEmitter.on("updateLocation", data => {
this.setState({ cityName: data });
});
}
handleChange() {
const { eventEmitter } = this.props;
const { cityName } = this.state;
eventEmitter.emit("updateLocation", cityName);
}
render() {
const {
isLoading,
name,
temperature,
weather_descriptions,
weather_icons,
observation_time,
country
} = this.state;
return (
<div className="main-container">
<div className="first-container">
<div className="wrapper">
{isLoading && <h3>Loading ...</h3>}
<NavigationBar />
{!isLoading && (
<WeatherComponent
className="weather-container"
name={name}
temperature={temperature}
weather_descriptions={weather_descriptions}
weather_icons={weather_icons}
observation_time={observation_time}
country={country}
/>
)}
<Dropdown
placeholder="Select location"
search
selection
defaultValue={this.state.cityName}
options={locationOptions.map(item => {
return {
key: item.key,
value: item.value,
text: item.text
};
})}
onChange={this.handleChange}
value={locationOptions.value}
/>
</div>
</div>
</div>
);
}
}
export default App;
store.js class
import React from "react";
import { EventEmitter } from "events";
export default class Store extends React.Component {
constructor(props) {
super(props);
this.eventEmitter = new EventEmitter();
// Main App State
this.state = {
appName: "Weather App"
};
}
render() {
return React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
...this.state,
eventEmitter: this.eventEmitter
});
});
}
}
WeatherComponent.js
import React from "react";
import "../sass/weather.scss";
import sunnyIcon from "../assets/sunnyicon.png";
import sun from "../assets/sunicon.png";
export default class WeatherComponent extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
// weather_descriptions i have to find a better icon for current weather
render() {
const {
temperature,
weather_descriptions,
observation_time,
name,
country
} = this.props;
return (
<div className="weather-container">
<div className="location-container">
<img src={sunnyIcon} className="logo2" alt="" />
<h1 className="total-weather-report">Today's weather report</h1>
<h1 className="location">{`${name}, ${country}`}</h1>
</div>
<div className="degree-container">
<img src={sunnyIcon} className="weather-logo2" alt="" />
<h2 className="degree-value">{`${temperature}°C`}</h2>
</div>
<div className="info-container">
<h2 className="local-weather-report">Local Weather Report</h2>
<div className="hr"></div>
<img src={sun} className="sun-icon" alt="" />
<h2 className="day">Sunday</h2>
<h2 className="weather-type">{weather_descriptions}</h2>
<h2 className="last-observation">Last observed on:</h2>
<h2 className="observation-time">{observation_time}</h2>
</div>
<div className="weekly-weather"></div>
</div>
);
}
}
When I run the app everything works but when I try to change the city from the dropdown, it crashes and this error pops-up.
The error
EventEmitter is part of the NodeJS API, is not available for browsers.
EDIT:
In App.jsx you have a function called "handleChange", that function should do the same thing you are doing on "componenDidMount" but using the actual value of the Dropdown, you don't need to manually create events.
Hope it helps

Categories

Resources