Cannot read property 'ArtisanID' of undefined when I reload the page - javascript

When I reload the page, I get this error:Cannot read property 'ArtisanID' of undefined.
How to fix this ?
I am new to this framework and thought it might be because the component instructions are not rendered when refreshing the page.
I'm using 3 context so I can't use React.Component classes unless I combine all 3 but I don't think that's a good idea.
import React, { useContext } from 'react'
import { Link } from 'react-router-dom'
import { ProductContext } from '../../global/ProductContext'
import { ArtisanContext } from '../../global/ArtisanContext'
import ProductCard from './ProductCard'
import { CartContext } from '../../global/CartContext'
const ProductPage = (props) => {
//props.preventDefault();
const {products} = useContext(ProductContext)
const {artisans} = useContext(ArtisanContext)
const { dispatch } = useContext(CartContext);
let productIDFind = props.match.params.id
const product = products.find(x=> x.ProductID === productIDFind)
const artisan = artisans.find(x=> x.ArtisanName === product.ProductArtisan)
const otherProducts = []
products.forEach(product => {
if (artisan.ArtisanName === product.ProductArtisan) {
otherProducts.push(<ProductCard key={product.ProductID} product={product} />)
}
});
return (
<section className="productPage main-section">
<div className="productPage-container main-section-constainer">
<Link className="artisanLink" to={'/Artisans/' + artisan.ArtisanID}>
<h1>Par <span>{artisan.ArtisanName}</span></h1>
</Link>
<div className="row">
<div className="productPage-principaleImg bg-img" style={{ backgroundImage: `url(${product.ProductProfilePicture})` }}></div>
<div className="productPage-info col">
<div className="row">
<div className="col">
<h2 className="productPage-Name">{product.ProductName}</h2>
<p className="productPage-Description">{product.ProductDescription}</p>
</div>
<div className="row">
<div className="productPage-Price">{product.ProductPrice}€</div>
<button className="addBtn" onClick={(e) => {
e.preventDefault();
dispatch({ type: 'ADD_TO_CART', id: product.ProductID, product })}}>
<span>Ajouter</span>
<span className="addImg bg-img"></span>
</button>
</div>
</div>
<div className="productPage-moreImg row">
<a className="productPage-YT-link" rel="noreferrer" href={product.ProductYT} target="_blank">
<div className="productPage-YT bg-img" style={{ backgroundImage: `url(${product.ProductProfilePicture})` }}></div>
<div className="productPage-YT-img bg img"></div>
</a>
<div className="productPage-img bg-img" style={{ backgroundImage: `url(${product.ProductSecondImg})` }}></div>
<div className="productPage-img bg-img" style={{ backgroundImage: `url(${product.ProductTreeImg})` }}></div>
</div>
</div>
</div>
<h3>Autre produits de {artisan.ArtisanName}</h3>
<div className="otherProducts">
{otherProducts}
</div>
</div>
</section>
)
}
export default ProductPage
ArtisanContext
import React, { createContext } from 'react'
import { db } from '../data/firebase'
export const ArtisanContext = createContext();
export class ArtisanContextProvider extends React.Component{
// Définition du state initial avec un tableau vide
state = {
artisans:[]
}
componentDidMount(){
const prevArtisans = this.state.artisans;
db.collection('Artisans').onSnapshot(snapshot => {
let changes = snapshot.docChanges();
changes.forEach(change => {
if (change.type === 'added') {
prevArtisans.push({
ArtisanID: change.doc.id,
ArtisanName: change.doc.data().ArtisanName,
ArtisanNbrProduits: change.doc.data().ArtisanNbrProduits,
ArtisanCategorie: change.doc.data().ArtisanCategorie,
ArtisanDescription: change.doc.data().ArtisanDescription,
ArtisanProfilePicture: change.doc.data().ArtisanProfilePicture,
ArtisanBanner: change.doc.data().ArtisanBanner
})
}
this.setState({
artisans: prevArtisans
})
})
})
}
render() {
return (
<ArtisanContext.Provider value={{artisans:[...this.state.artisans]}} >
{this.props.children}
</ArtisanContext.Provider>
)
}
}

I saw artisan was decalre by artisans.find. The result of artisans.find can be undefined if no item was found. So this issue will occur. To avoid this issue, you can use optional chaining:
<Link className="artisanLink" to={'/Artisans/' + artisan?.ArtisanID}>
<h1>Par <span>{artisan?.ArtisanName}</span></h1>

Related

I don't know why I get this, if it is according to the React manual

I tell him I am transferring an event from the component
child ( ItemCount) to the parent component parent ItemDetail the onADD event that only acts if an item is passed to it and when it does, the state becomes true.
The child has an event called add to cart which triggers the event and passes a product counter.
It runs perfectly but it throws me a warning that is the following.
react-dom.development.js:86 Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
Can you tell me the mistake and what I did wrong? from now very grateful
I share the codes Thanks
ItemCount (component child)
import React, { useState, useContext} from 'react';
import 'materialize-css/dist/css/materialize.css';
import '../App.css';
import {FontAwesomeIcon} from '#fortawesome/react-fontawesome';
import {faPlus, faMinus, faPowerOff} from '#fortawesome/free-solid-svg-icons';
import {contextoProducto} from './ProductContext';
import swal from 'sweetalert';
const ItemCount = ({item, stockInitial, initial = 0, onAdd}) => {
const [contador, setContador] = useState(initial)
const [stock, setStock] = useState(stockInitial)
const { addProduct } = useContext(contextoProducto);
const sumar = () => {
setContador(contador + 1)
setStock(stock - 1);
avisarStock();
}
const restar= () => {
if(contador > 0){
setContador(contador - 1);
setStock(stock + 1);
}
else
{
setContador(0);
}
}
const reset = () =>{
setContador(0);
setStock(stockInitial);
}
const avisarStock = () => {
if(stock > 0 ){
}
else{
swal('No podemos enviar su envio no hay stock', "Gracias", "error");
setStock(0);
setContador(contador)
}
}
const agregarAlCarrito = () => {
onAdd(contador);
}
return(
<div>
<div className=" row left text">Stock: {stock}</div>
<article>{contador}</article>
<div className="buttonCount">
<button onClick={sumar}>
<FontAwesomeIcon icon ={faPlus}/>
</button>
<button onClick={restar}>
<FontAwesomeIcon icon={faMinus}/>
</button>
<button onClick={reset}>
<FontAwesomeIcon icon={faPowerOff}/>
</button>
<br/><h2>{avisarStock}</h2>
<button onClick={() => addProduct({...item, quantity: contador}) || agregarAlCarrito()} > Agregar al carrito </button>
</div>
</div>
)
}
export default ItemCount;
ItemDetail (component father)
import React, { useState } from "react";
import '../App.css';
import 'materialize-css/dist/css/materialize.css';
import Count from './ItemCount';
import { Link } from "react-router-dom";
export const ItemDetail = ({item}) => {
const [itemSell, setItemSell] = useState(false);
const onAdd = (count) => {
setItemSell(true);
}
return (
<main className="itemsdetails">
<div className="row" id= {item.id}>
<div className="col s12 m6">
<img src={item.image} alt="item" className="itemImg responsive-img"/>
</div>
<div className="col s12 m6">
<div className="col s12">
<h5 className="itemName">{item.title}</h5>
</div>
<div className="col s12">
<p className="itemDescription">{item.description}</p>
</div>
<div className="col s12">
<p className="itemPrice"> {item.price}</p>
</div>
<div className="col s12">
{
itemSell ? <Link to="/cart"><button className="waves-effect waves-light btn-large">Finalizar Compra</button></Link> : <Count item= {item} stockInitial={item.stock} onAdd= { onAdd } />
}
</div>
</div>
</div>
</main>
)
};
export default ItemDetail;
<br/><h2>{avisarStock}</h2>
Here, you are trying to render a component, but actually avisarStock is a function which sets state and opens an alert. It makes no sense to try to render this function.
It would appear you meant to render stock not avisarStock. This would show your stock state in the <h2>:
<br/><h2>{stock}</h2>
<br/><h2>{avisarStock}</h2> avisarStock is a function, and since Component can be function, react thinks you are doing Component instead of <Component />

React conditional styling in a map function problem

I just want to show toggled item. But all map items showing up. Basically this is the result I'm getting from onclick. I think i need to give index or id to each item but i don't know how to do it. i gave id to each question didn't work.
App.js.
import "./App.css";
import React, { useState, useEffect } from "react";
import bg from "./images/bg-pattern-desktop.svg";
import bg1 from "./images/illustration-box-desktop.svg";
import bg2 from "./images/illustration-woman-online-desktop.svg";
import { data } from "./data";
import Faq from "./Faq";
function App() {
const [db, setDb] = useState(data);
const [toggle, setToggle] = useState(false);
useEffect(() => {
console.log(db);
}, []);
return (
<>
<div className="container">
<div className="container-md">
<div className="faq">
<img src={bg} className="bg" />
<img src={bg1} className="bg1" />
<img src={bg2} className="bg2" />
<div className="card">
<h1>FAQ</h1>
<div className="info">
{db.map((dat) => (
<Faq
toggle={toggle}
setToggle={setToggle}
title={dat.title}
desc={dat.desc}
key={dat.id}
id={dat.id}
/>
))}
</div>
</div>
</div>
</div>
</div>
</>
);
}
export default App;
(map coming from simple data.js file that I created. it includes just id title desc.)
Faq.js
import React from "react";
import arrow from "./images/icon-arrow-down.svg";
const Faq = ({ toggle, setToggle, title, desc, id }) => {
return (
<>
{" "}
<div className="question" onClick={() => setToggle(!toggle)}>
<p>{title}</p>
<img src={arrow} className={toggle ? "ikon aktif" : "ikon"} />
</div>
<p className="answer border">{toggle ? <>{desc}</> : ""}</p>
</>
);
};
export default Faq;
You need to store the index value of the toggle item.
You can modify the code with only 2 lines with the existing codebase.
import "./App.css";
import React, { useState, useEffect } from "react";
import bg from "./images/bg-pattern-desktop.svg";
import bg1 from "./images/illustration-box-desktop.svg";
import bg2 from "./images/illustration-woman-online-desktop.svg";
import { data } from "./data";
import Faq from "./Faq";
function App() {
const [db, setDb] = useState(data);
const [toggle, setToggle] = useState(-1); //Modify Here
useEffect(() => {
console.log(db);
}, []);
return (
<>
<div className="container">
<div className="container-md">
<div className="faq">
<img src={bg} className="bg" />
<img src={bg1} className="bg1" />
<img src={bg2} className="bg2" />
<div className="card">
<h1>FAQ</h1>
<div className="info">
{db.map((dat, index) => ( //Modify Here
<Faq
toggle={index === toggle} //Modify Here
setToggle={() => setToggle(index)} //Modify Here
title={dat.title}
desc={dat.desc}
key={dat.id}
id={dat.id}
/>
))}
</div>
</div>
</div>
</div>
</div>
</>
);
}
export default App;
import React from "react";
import arrow from "./images/icon-arrow-down.svg";
const Faq = ({ toggle, setToggle, title, desc, id }) => {
return (
<>
{" "}
<div className="question" onClick={setToggle}>
<p>{title}</p>
<img src={arrow} className={toggle ? "ikon aktif" : "ikon"} />
</div>
<p className="answer border">{toggle ? <>{desc}</> : ""}</p>
</>
);
};
export default Faq;
You will need state for each toggle. Here is a minimal verifiable example. Run the code below and click ⭕️ to toggle an item open. Click ❌ to close it.
function App({ faq = [] }) {
const [toggles, setToggles] = React.useState({})
const getToggle = key =>
Boolean(toggles[key])
const setToggle = key => event =>
setToggles({...toggles, [key]: !getToggle(key) })
return faq.map((props, key) =>
<Faq key={key} {...props} open={getToggle(key)} toggle={setToggle(key)} />
)
}
function Faq({ question, answer, open, toggle }) {
return <div>
<p>
{question}
<button onClick={toggle} children={open ? "❌" : "⭕️"} />
</p>
{open && <p>{answer}</p>}
</div>
}
const faq = [
{question: "hello", answer: "world"},
{question: "eat", answer: "vegetables"}
]
ReactDOM.render(<App faq={faq} />, document.querySelector("#app"))
p { border: 1px solid gray; padding: 0.5rem; }
p ~ p { margin-top: -1rem; }
button { float: right; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Instead of doing this (in App component):
const [db, setDb] = useState(data);
const [toggle, setToggle] = useState(false);
you can write an useState hook like below to combine the two hooks and assign an isOpened property for each Faq element:
const [db, setDb] = useState(data.map(value=>{return {...value, isOpened:false}}));
and then right here you can do this (as the child of <div className="info">):
{db.map((dat, index) => (
<Faq
toggle={dat.isOpened}
setToggle={() => toggleById(dat.id)}
title={dat.title}
desc={dat.desc}
key={dat.id}
id={dat.id}
/>
))}
Also you need to declare toggleById function in App component:
const toggleById = (id) => {
const newDb = db.map(dat=>{
if(dat.id==id){
return {...dat,isOpened:!dat.isOpened}
}
return dat;
});
setDb(newDb);
}
and since setToggle prop of Faq, calls toggleById by its defined parameter, there is no need to do this in Faq component:
<div className="question" onClick={() => setToggle(!toggle)}>
you can simply write:
<div className="question" onClick={setToggle}>

React reset state after filtering with onClick

I have a basic app which simply returns a number of cards with some content inside, I have some buttons which then filter the returned cards by a value found in the dataset. The filter buttons do work indididually but if I click one after the other the filter is being applied to the now filtered data. How can I make sure the filter is being applied to the initial state of the data or how can I reset the state to everything before the filter is applied? Thanks.
parent.js
import './App.scss';
import DataThoughts from "./assets/data/DataThoughts";
import Thoughts from "./components/Thoughts";
function AppThoughts() {
return (
<div className="App">
<main className={'bg-light'}>
<div className={'container'}>
<Thoughts data={DataThoughts} />
</div>
</main>
</div>
);
}
export default AppThoughts;
Thoughts.js
import React, { Component } from 'react';
import FilterButton from "./FilterButton";
class Thoughts extends Component {
constructor(props) {
super(props);
this.state = {...props};
}
handleClick = value => () => {
let filtered = this.state.data.filter(item => item.Emotion === value);
this.setState({ data: filtered });
console.log(this.state.data);
};
render() {
let data = this.state.data;
let numberOfThoughts = data.length;
let dataList = this.state.data.map((thought, i) =>
<div className={`col-12 col-sm-12 col-md-6 ${i % 2 === 0 ? i % 3 === 0 ? 'col-lg-3 col-xl-3' : 'col-lg-5 col-xl-5' : 'col-lg-4 col-xl-4'}`} key={'thought'+i}>
<div className="card mb-4">
{thought.Photo ? <img src={thought.Photo} className="card-img-top" alt={thought.Emotion}/> : ''}
<div className="p-5">
<blockquote className="blockquote mb-0">
<p className={'small text-muted'}>{thought.Date}</p>
<p className={`${i % 2 === 0 ? i % 3 === 0 ? 'display-6' : 'display-4' : 'display-5'} mb-4`}>{thought.Thought}</p>
<footer className="small text-muted">{thought.Author}, <cite title="Source Title">{thought.Location}</cite></footer>
</blockquote>
</div>
</div>
</div>
);
return (
<section className="row section-row justify-content-start thoughts">
<div className={`col-12`} key={'filters'}>
<FilterButton buttonText={"Happy"} onClick={this.handleClick('Happy')} />
<FilterButton buttonText={"Sad"} onClick={this.handleClick('Sad')} />
<FilterButton buttonText={"Thinking"} onClick={this.handleClick('Thinking')} />
<FilterButton buttonText={"All"} onClick={this.handleClick('All')} />
</div>
{dataList}
<div className={`col-12 col-sm-12 col-md-6 col-lg-2 col-xl-2 d-flex align-items-center justify-content-center`} key={'total'}>
<span className={'display-1'}>{numberOfThoughts}</span>
</div>
</section>
);
}
}
Thoughts.defaultProps = {
Photo: '',
Emotion:'Happy',
Date:'Morning',
Thought:'Default',
Author:'Me',
Location:'Somewhere'
};
export default Thoughts; // Don’t forget to use export default!
FilterButtons.js
import React, { Component } from 'react';
class FilterButton extends Component {
render() {
return (
<button className={'btn btn-primary d-inline-block mb-4'} onClick={this.props.onClick}>{this.props.buttonText}</button>
);
}
}
export default FilterButton; // Don’t forget to use export default!
It looks like the initial data comes from the props. You can access the props with this.props. So you can do something like this:
handleClick = value => () => {
// filter the initial data
let filtered = this.props.data.filter(item => item.Emotion === value);
this.setState({ data: filtered });
// set to initial data
// this.setState({ ...this.props });
console.log(this.state.data);
};

How to add the product to the favorites?

I am currently making a project over the database I created using Mock API. I created a button, created addToFavorites function. When the button was clicked, I wanted the selected product's information to go to the favorites, but I couldn't. I would be glad if you could help me on how to do this.
(Favorites.js empty now. I got angry and deleted all the codes because I couldn't.)
(
Recipes.js
import React, { useState, useEffect } from "react"
import axios from "axios"
import "./_recipe.scss"
import Card from "../Card"
function Recipes() {
const [recipes, setRecipes] = useState([])
const [favorites, setFavorites] = useState([])
useEffect(() => {
axios
.get("https://5fccb170603c0c0016487102.mockapi.io/api/recipes")
.then((res) => {
setRecipes(res.data)
})
.catch((err) => {
console.log(err)
})
}, [])
const addToFavorites = (recipes) => {
setFavorites([...favorites, recipes])
console.log("its work?")
}
return (
<div className="recipe">
<Card recipes={recipes} addToFavorites={addToFavorites} />
</div>
)
}
export default Recipes
Card.js
import React, { useState } from "react"
import { Link } from "react-router-dom"
import { BsClock, BsBook, BsPerson } from "react-icons/bs"
function Card({ recipes, addToFavorites }) {
const [searchTerm, setSearchTerm] = useState("")
return (
<>
<div className="recipe__search">
<input
type="text"
onChange={(event) => {
setSearchTerm(event.target.value)
}}
/>
</div>
<div className="recipe__list">
{recipes
.filter((recipes) => {
if (searchTerm === "") {
return recipes
} else if (
recipes.title.toLowerCase().includes(searchTerm.toLowerCase())
) {
return recipes
}
})
.map((recipe) => {
return (
<div key={recipe.id} className="recipe__card">
<img src={recipe.image} alt="foods" width={350} height={230} />
<h1 className="recipe__card__title">{recipe.title}</h1>
<h3 className="recipe__card__info">
<p className="recipe__card__info--icon">
<BsClock /> {recipe.time} <BsBook />{" "}
{recipe.ingredientsCount} <BsPerson />
{recipe.servings}
</p>
</h3>
<h3 className="recipe__card__desc">
{recipe.description.length < 100
? `${recipe.description}`
: `${recipe.description.substring(0, 120)}...`}
</h3>
<button type="button" className="recipe__card__cta">
<Link
to={{
pathname: `/recipes/${recipe.id}`,
state: { recipe }
}}
>
View Recipes
</Link>
</button>
<button onClick={() => addToFavorites(recipes)}>
Add to favorites
</button>
</div>
)
})}
</div>
</>
)
}
export default Card
Final Output:
I have implemented the addToFavorite() and removeFavorite() functionality, you can reuse it the way you want.
I have to do bit of modification to the code to demonstrate its working, but underlying functionality of addToFavorite() and removeFavotie() works exactly the way it should:
Here is the Card.js where these both functions are implemented:
import React, { useState } from "react";
import { BsClock, BsBook, BsPerson } from "react-icons/bs";
function Card({ recipes }) {
const [searchTerm, setSearchTerm] = useState("");
const [favorite, setFavorite] = useState([]); // <= this state holds the id's of all favorite reciepies
// following function handles the operation of adding fav recipes's id's
const addToFavorite = id => {
if (!favorite.includes(id)) setFavorite(favorite.concat(id));
console.log(id);
};
// this one does the exact opposite, it removes the favorite recipe id's
const removeFavorite = id => {
let index = favorite.indexOf(id);
console.log(index);
let temp = [...favorite.slice(0, index), ...favorite.slice(index + 1)];
setFavorite(temp);
};
// this variable holds the list of favorite recipes, we will use it to render all the fav ecipes
let findfavorite = recipes.filter(recipe => favorite.includes(recipe.id));
// filtered list of recipes
let filtered = recipes.filter(recipe => {
if (searchTerm === "") {
return recipe;
} else if (recipe.title.toLowerCase().includes(searchTerm.toLowerCase())) {
return recipe;
}
});
return (
<div className="main">
<div className="recipe__search">
<input
type="text"
onChange={event => {
setSearchTerm(event.target.value);
}}
/>
</div>
<div className="recipe-container">
<div className="recipe__list">
<h2>all recipes</h2>
{filtered.map(recipe => {
return (
<div key={recipe.id} className="recipe__card">
<img src={recipe.image} alt="foods" width={50} height={50} />
<h2 className="recipe__card__title">{recipe.title}</h2>
<h4 className="recipe__card__info">
<p>
<BsClock /> {recipe.time} <BsBook />{" "}
{recipe.ingredientsCount} <BsPerson />
{recipe.servings}
</p>
</h4>
<h4 className="recipe__card__desc">
{recipe.description.length < 100
? `${recipe.description}`
: `${recipe.description.substring(0, 120)}...`}
</h4>
<button onClick={() => addToFavorite(recipe.id)}>
add to favorite
</button>
</div>
);
})}
</div>
<div className="favorite__list">
<h2>favorite recipes</h2>
{findfavorite.map(recipe => {
return (
<div key={recipe.id} className="recipe__card">
<img src={recipe.image} alt="foods" width={50} height={50} />
<h2 className="recipe__card__title">{recipe.title}</h2>
<h4 className="recipe__card__info">
<p className="recipe__card__info--icon">
<BsClock /> {recipe.time} <BsBook />{" "}
{recipe.ingredientsCount} <BsPerson />
{recipe.servings}
</p>
</h4>
<h4 className="recipe__card__desc">
{recipe.description.length < 100
? `${recipe.description}`
: `${recipe.description.substring(0, 120)}...`}
</h4>
<button onClick={() => removeFavorite(recipe.id)}>
remove favorite
</button>
</div>
);
})}
</div>
</div>
</div>
);
}
export default Card;
Here is the live working app : stackblitz
You can get the previous favourites recipes and add the new ones.
const addToFavorites = (recipes) => {
setFavorites(prevFavourites => [...prevFavourites, recipes])
console.log("its work?")
}

How to call an action from a component without connecting it to the redux store?

I have a Cards component which takes in the props from the UserPostscomponent(which is connected to the store) and displays cards. Cards is not connected to the redux store and I want to dispatch an action in the handleDelete function. How can I do that?
import React, { Component } from "react"
class Cards extends Component {
handleDelete = (id) => {
}
render() {
const { title, description } = this.props.post
const { postId } = this.props.post._id
return (
<div className="card">
<div className="card-content">
<div className="media">
<div className="media-left">
<figure className="image is-48x48">
<img
src="https://bulma.io/images/placeholders/96x96.png"
alt="Placeholder image"
/>
</figure>
</div>
<div className="media-content" style={{border: "1px grey"}}>
<p className="title is-5">{title}</p>
<p className="content">{description}</p>
<button className="button is-success">Edit</button>
<button onClick={this.handleDelete(postId)} className="button is-success">Delete</button>
</div>
</div>
</div>
</div>
)
}
}
export default Cards
UserPosts component which passes the props
<div>
{userPosts &&
userPosts.map(post => {
return <Cards key={post._id} post={post} />
})}
</div>
```
You can use the global store and directly call dispatch method. Not recommended. Hard to maintain and debug.
import { createStore } from 'redux'
const store = createStore(todos, ['Use Redux'])
// Dont create new one, use the one created in root
function addTodo(text) {
return {
type: 'ADD_TODO',
text
}
}
store.dispatch(addTodo('Read the docs'))
store.dispatch(addTodo('Read about the middleware'))

Categories