Front-End Filtering React JS - reactjs

I have filtered the products and on submitting the search term, am showing the results in a new page using history.push() property.
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { IoIosSearch } from 'react-icons/io';
import { useHistory } from "react-router-dom";
import './style.css';
/**
* #author
* #function Search
*/
const Search = (props) => {
const product = useSelector(state => state.product);
let { products , filteredProducts } = product;
const [searchTerm, setSearchTerm] = useState('');
const onChangeSearch = (e) => {
setSearchTerm(e.currentTarget.value);
}
const isEmpty = searchTerm.match(/^\s*$/);
if(!isEmpty) {
filteredProducts = products.filter( function(prod) {
return prod.name.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase().trim())
})
}
const history = useHistory();
const display = !isEmpty
const handleSubmit =(e) => {
e.preventDefault();
if( !isEmpty ) {
history.push(`/search/search_term=${searchTerm}/`, { filteredProducts })
}
setSearchTerm('');
}
return (
<div>
<form onSubmit={handleSubmit}>
<div className="searchInputContainer">
<input
className="searchInput"
placeholder={'What are you looking for...'}
value={searchTerm}
onChange={onChangeSearch}
/>
<div className="searchIconContainer">
<IoIosSearch
style={{
color: 'black',
fontSize: '22px'
}}
onClick={handleSubmit}
/>
</div>
</div>
</form>
{
display && <div className="searchResultsCont">
{filteredProducts.map((prod, index) => (<div key={index}>{prod.name}</div>))}
</div>
}
</div>
);
}
export default Search
On the new page this is the code :
import React from 'react';
import Layout from '../../components/Layout';
const SearchScreen = ({location}) => {
const products = location.state.filteredProducts;
const show = products.length > 0
return (
<Layout>
<div>
{
show ? products.map((prod, index) => (<div key={index}>{prod.name}</div>)) : <div>No items found</div>
}
</div>
</Layout>
)
}
export default SearchScreen
The problem comes when I copy and paste the URL to another new page, or like when I email others the URL the error becomes " Cannot read property 'filteredProducts' of undefined ". Using this method I understand that the results (filtered products) have not been pushed through the function history.push() that's why it is undefined, how can I make this possible?

I changed the whole aspect to filtering the products from the back-end..
It worked

Related

rick and morty infinity loop on query

hello im using rick & morty API, im trying to filter by location id i can search by id but if i clear the input and press enter the page loops and never render my data
im using a custom hook to call the API from inputs.
import axios from 'axios'
import React, { useEffect, useState } from 'react'
const useFetch = (URL) => {
const [response, setResponse] = useState()
useEffect(() => {
axios.get(URL)
.then(res => setResponse(res.data))
.catch(err => console.log(err))
}, [URL])
return response
}
export default useFetch
here im rendering the characters from rick and morty
import React from 'react'
import useFetch from '../hooks/useFetch'
const CardResidents = ({url}) => {
const residents = useFetch(url)
const bgColor = {
}
if (residents?.status === "Dead") {
bgColor.backgroundColor = "red"
}else if (residents?.status === "Alive") {
bgColor.backgroundColor = "green"
}else {
bgColor.backgroundColor = "gray"
}
return (
<div className='resident'>
<div className='status'><div className='circle' style={bgColor}></div> {residents?.status}</div>
<img className='resident-img' src={residents?.image} alt="" />
<h2 className='resident-name'>{residents?.name }</h2>
<hr />
<p className='resident-info'>Type</p>
<p className='resident-fetch'>{residents?.type }</p>
<p className='resident-info'>Origin</p>
<p className='resident-fetch'>{residents?.origin.name }</p>
<p className='resident-info'>Appearance in episodes</p>
<p className='resident-fetch'>{residents?.episode.length}</p>
</div>
)
}
export default CardResidents
this is the input im using to search location by id, but everytime y clear my input the page starts an infinity loop
import React from 'react'
const Query = ({setQueryFetch}) => {
const submitQuery = (e) => {
e.preventDefault()
const test = e.target.query.value
setQueryFetch(test)
console.log( test + "query")
}
return (
<form action="" onSubmit={submitQuery}>
<input id='query' type="text" placeholder='Search for locations between 1 - 126'/>
</form>
)
}
export default Query
and this is my main component where im renderin everything, im trying to select if the data will render by the randomID, or my query
import { useState } from 'react'
import './App.css'
import CardResidents from './components/CardResidents'
import LocationInfo from './components/LocationInfo'
import useFetch from './hooks/useFetch'
import banner from '../public/rick-morty-banner.jpg'
import Query from './components/Query'
function App() {
const randomId = Math.ceil(Math.random() * (126 - 1) +1)
const [queryFetch, setQueryFetch] = useState("")
let location = useFetch(`https://rickandmortyapi.com/api/location/${randomId}`)
if (queryFetch != "") {
console.log(queryFetch + " app")
location = useFetch(`https://rickandmortyapi.com/api/location/${queryFetch}`)
}else {
location = useFetch(`https://rickandmortyapi.com/api/location/${randomId}`)
}
return (
<div className="App">
<img className='banner' src={banner} alt="" />
<Query setQueryFetch={setQueryFetch}/>
<LocationInfo location={location}/>
<div className='resident-container'>
{
location?.residents.map(url =>(
<CardResidents
key={url}
url={url}
/>
))
}
</div>
</div>
)
}
export default App

React - pass context to SweetAlert popup

My context is as follows:
import React, {createContext, useEffect, useState} from "react";
export const CartContext = createContext();
const CartContextProvider = (props) => {
const [cart, setCart] = useState(JSON.parse(localStorage.getItem('cart')) || []);
useEffect(() => {
localStorage.setItem('cart', JSON.stringify(cart));
}, [cart]);
const updateCart = (productId, op) => {
let updatedCart = [...cart];
if (updatedCart.find(item => item.id === productId)) {
let objIndex = updatedCart.findIndex((item => item.id === productId));
if (op === '-' && updatedCart[objIndex].qty > 1) {
updatedCart[objIndex].qty -= 1;
} else if (op === '+') {
updatedCart[objIndex].qty += 1;
}
} else {
updatedCart.push({id: productId, qty: 1})
}
setCart(updatedCart);
}
const removeItem = (id) => {
setCart(cart.filter(item => item.id !== id));
};
return (
<CartContext.Provider value={{cart, updateCart, removeItem}}>
{props.children}
</CartContext.Provider>
)
};
export default CartContextProvider;
App.js:
import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import NavigationBar from "./components/layout/navigationBar/NavigationBar";
import Homepage from "./pages/homepage/Homepage";
import AboutUsPage from "./pages/aboutUs/AboutUsPage";
import ContactPage from "./pages/contact/ContactPage";
import SearchPage from "./pages/search/SearchPage";
import ShoppingCart from "./components/layout/shoppingCart/ShoppingCart";
import CartContextProvider from "./context/CartContext";
function App() {
return (
<div>
<CartContextProvider>
<Router>
<NavigationBar/>
<ShoppingCart/>
<Routes>
<Route exact path="/" element={<Homepage/>}/>
<Route path="/a-propos" element={<AboutUsPage/>} />
<Route path="/contact" element={<ContactPage/>}/>
<Route path="/recherche" element={<SearchPage/>}/>
</Routes>
</Router>
</CartContextProvider>
</div>
);
}
export default App;
In the component ShoppingCart I am using another component ShoppingCartQuantity which in turn makes use of the context. It works as it should.
Here's the ShoppingCartQuantity component:
import React, {useContext} from "react";
import {CartContext} from "../../../context/CartContext";
import styles from './ShoppingCartQuantity.module.css'
const ShoppingCartQuantity = ({productId}) => {
const {cart, updateCart} = useContext(CartContext);
let qty = 0;
if (cart.find((item => item.id === productId))) {
let objIndex = cart.findIndex((item => item.id === productId));
qty = cart[objIndex].qty;
}
return (
<div>
<span>
<span className={`${styles.op} ${styles.decrementBtn}`} onClick={() => updateCart(productId, '-')}>-</span>
<span className={styles.qty}>{qty}</span>
<span className={`${styles.op} ${styles.incrementBtn}`} onClick={() => updateCart(productId, '+')}>+</span>
</span>
</div>
)
}
export default ShoppingCartQuantity;
Now I am trying to use the ShoppingCartQuantity component in the Homepage component which is a route element (refer to App.js) but getting the error Uncaught TypeError: Cannot destructure property 'cart' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is undefined.
So the context is working for components outside the router but not for those inside it. If I have wrapped the router within the provider, shouldn't all the route elements get access to the context or am I missing something?
UPDATE
As user Build Though suggested in the comments, I tried using the ShoppingCartQuantity component in another route element and it works fine; so the problem is not with the router!
Below is the code of how I am using the ShoppingCartQuantity component in the Homepage component:
import React, { useState, useEffect, useRef } from "react";
import { Responsive, WidthProvider } from "react-grid-layout";
import Subcat from "../../components/subcat/Subcat";
import CategoryService from "../../services/api/Category";
import SubCategoryService from "../../services/api/SubCategory";
import CategoriesLayout from "../../utils/CategoriesLayout";
import CategoryCard from "../../components/category/CategoryCard";
import { Triangle } from 'react-loader-spinner'
import ScrollIntoView from 'react-scroll-into-view'
import ProductService from "../../services/api/Product";
import Swal from 'sweetalert2'
import withReactContent from 'sweetalert2-react-content';
import YouTube from 'react-youtube';
import FavoriteBtn from "../../components/favorite/FavoriteBtn";
import ShoppingCartQuantity from "../../components/layout/shoppingCart/ShoppingCartQuantity";
import "./Homepage.css";
import "../../components/product/ProductModal.css"
import "react-loader-spinner";
import modalStyles from "../../components/product/ProductModal.module.css"
function Homepage() {
const [categories, setCategories] = useState([]);
const [subCats, setSubCats] = useState([]);
const [loader, setLoader] = useState(false);
const ResponsiveGridLayout = WidthProvider(Responsive);
const scrollRef = useRef();
const productModal = withReactContent(Swal);
const opts = {
// height: '390',
// width: '640',
playerVars: {
autoplay: 1,
}
};
useEffect(() => {
CategoryService.get().then((response) => {
setCategories(response);
});
}, []);
function showSubCatsHandler(catId) {
setLoader(true);
setSubCats([]);
SubCategoryService.get(catId).then((response) => {
setSubCats(response.data);
setLoader(false);
scrollRef.current.scrollIntoView({ behavior: "smooth" });
});
}
function showProductPopupHandler(productId) {
ProductService.get(productId).then((response) => {
const product = response.data;
return productModal.fire({
html:
<div>
<h3 className={modalStyles.header}>{product.AMP_Title}</h3>
<h4 className={`${modalStyles.price} ${modalStyles.header}`}>{"CHf " + product.AMP_Price}</h4>
<img className={modalStyles.image} src={process.env.REACT_APP_BACKEND_BASE_URL + 'images/products/' + product.AMP_Image} />
{
product.descriptions.map((desc, _) => (
<div key={desc.AMPD_GUID}>
{
desc.AMPD_Title === '1' && <h4 className={modalStyles.header}>{product.AMP_Title}</h4>
}
{
desc.AMPD_Image !== '' && <img src={process.env.REACT_APP_BACKEND_BASE_URL + 'images/descriptions/' + desc.AMPD_Image} className={desc.AMPD_Alignment === 'left' ? modalStyles.descImageLeft : modalStyles.descImageRight} />
}
<p className={modalStyles.description}>{desc.AMPD_Description}</p>
</div>
))
}
<br/>
<div>
<FavoriteBtn productId={product.AMP_GUID}/>
<ShoppingCartQuantity productId={product.AMP_GUID} />
</div>
<br/>
{
product.AMP_VideoId !== '' &&
<YouTube
videoId={product.AMP_VideoId}
opts={opts}
/>
}
</div>,
showConfirmButton: false,
showCloseButton: true
});
});
}
return (
<div>
<div className="categories-container">
<ResponsiveGridLayout
className="layout"
layouts={ CategoriesLayout }
breakpoints={ { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 } }
cols={ { lg: 8, md: 8, sm: 6, xs: 4, xxs: 2 } }
isDraggable={ false }
>
{
categories.map((cat, index) => (
<div key={index}>
<CategoryCard
category_id = {cat.AMC_GUID}
image = {cat.AMC_Image}
showSubCatsHandler = {showSubCatsHandler}
/>
</div>
))
}
</ResponsiveGridLayout>
{
loader &&
<Triangle
height="100"
width="100"
color='#bcad70'
ariaLabel='loading'
wrapperClass="loader"
/>
}
<div ref={scrollRef}>
{
Object.keys(subCats).map((keyName, _) => (
<Subcat
key={subCats[keyName].AMSC_GUID}
title={ subCats[keyName].AMSC_Title }
products={ subCats[keyName].products }
showProductPopupHandler = {showProductPopupHandler}
/>
))
}
</div>
</div>
</div>
);
}
export default Homepage;
I am using the component in a SweetAlert popup. I guess it's the SweetAlert component that is not getting access to the context. Does anyone have an idea how to pass the context to the SweetAlert component?
UPDATE 2
The accepted solution works great except for 1 small issue: the ShoppingCartQuantity component was not re-rendering inside the SweetAlert popup and the qty would not change visually.
I updated the component by using the qty as a state.
const ShoppingCartQuantity = ({ qty, productId, updateCart }) => {
const [quantity, setQuantity] = useState(qty);
const updateCartHandler = (productId, amount) => {
updateCart(productId, amount);
setQuantity(Math.max(quantity + amount, 1));
}
return (
<div>
<span>
<span
className={`${styles.op} ${styles.decrementBtn}`}
onClick={() => updateCartHandler(productId, -1)}
>
-
</span>
<span className={styles.qty}>{quantity}</span>
<span
className={`${styles.op} ${styles.incrementBtn}`}
onClick={() => updateCartHandler(productId, 1)}
>
+
</span>
</span>
</div>
)
}
Issue
It's very likely that the sweet alert component is rendered outside your app, and thus, outside the CartContextProvider provider. I just searched the repo docs if there is a way to specify a root element, but this doesn't seem possible since this sweet alert code isn't React specific.
See this other similar issue regarding accessing a Redux context in the alert.
Solution
It doesn't seem possible ATM to access the context value from within the modal, so IMHO a workaround could be to refactor your ShoppingCartQuantity component into a wrapper container component to access the context and a presentation component to receive the context values and any callbacks.
I suggest also just passing the amount you want to increment/decrement the quantity by to updateCart instead of passing a "+"/"-" string and operator comparison.
Example:
export const withShoppingCartContext = Component => props => {
const { cart, removeItem, updateCart } = useContext(CartContext);
return <Component {...props} {...{ cart, removeItem, updateCart }} />;
}
const ShoppingCartQuantity = ({ cart, productId, updateCart }) => {
const qty = cart.find(item => item.id === productId)?.qty ?? 0;
return (
<div>
<span>
<span
className={`${styles.op} ${styles.decrementBtn}`}
onClick={() => updateCart(productId, -1)}
>
-
</span>
<span className={styles.qty}>{qty}</span>
<span
className={`${styles.op} ${styles.incrementBtn}`}
onClick={() => updateCart(productId, 1)}
>
+
</span>
</span>
</div>
)
}
export default ShoppingCartQuantity;
In places in your app where ShoppingCartQuantity component is used within the CartContextProvider decorate it with the withShoppingCartContext HOC and use normally.
ShoppingCart
import ShoppingCartQuantityBase, {
withShoppingCartContext
} from "../../components/layout/shoppingCart/ShoppingCartQuantity";
const ShoppingCartQuantity = withShoppingCartContext(ShoppingCartQuantityBase);
const ShoppingCart = (props) => {
...
return (
...
<ShoppingCartQuantity productId={....} />
...
);
};
In places where ShoppingCartQuantity component is used outside the context, like in the sweet modal, access the context within the React code and pass in the context values and callbacks.
...
import ShoppingCartQuantity from "../../components/layout/shoppingCart/ShoppingCartQuantity";
...
function Homepage() {
...
const { cart, updateCart } = useContext(CartContext);
const productModal = withReactContent(Swal);
...
function showProductPopupHandler(productId) {
ProductService.get(productId)
.then((response) => {
const product = response.data;
return productModal.fire({
html:
<div>
...
<div>
<FavoriteBtn productId={product.AMP_GUID}/>
<ShoppingCartQuantity
productId={product.AMP_GUID}
{...{ cart, updateCart }}
/>
</div>
...
</div>,
showConfirmButton: false,
showCloseButton: true
});
});
}
return (...);
}
export default Homepage;
Additional Issues
Your context provider is mutating state when updating quantities. When updating nested state you should still create a shallow copy of the array elements that are being updated.
Example:
const CartContextProvider = (props) => {
...
const updateCart = (productId, amount) => {
// only update if item in cart
if (cart.some(item => item.id === productId)) {
// use functional state update to update from previous state
// cart.map creates shallow copy of previous state
setCart(cart => cart.map(item => item.id === productId
? {
...item, // copy item being updated into new object reference
qty: Math.max(item.qty + amount, 1), // minimum quantity is 1
}
: item
));
}
}
const removeItem = (id) => {
setCart(cart => cart.filter(item => item.id !== id));
};
return (
<CartContext.Provider value={{ cart, updateCart, removeItem }}>
{props.children}
</CartContext.Provider>
);
};
You did't show where you are using the ShoppingCart component or the ShoppingCartQuantity component.
Anyway, when you declare a route, you must pass the component, not the root element. So, this line:
<Route exact path="/" element={<Homepage/>}/>
must be
<Route exact path="/" component={Homepage}/>

How to perform search operation from Navbar when data is recieved from global Context API

I am executing search operation from Navbar component for the data that is present in separate Context API, and the results for the search operation will be presented in another component call Blog, which is using same Context API, but the problem here is search operation is not executing in real time, like when I clear the search bar then It's difficult to set search term in use state hook which is present in context API. So in this case how to solve the problem.
Below is my code from context API
import { BlogContext } from "./BlogContext";
import React from "react";
import { useState } from "react";
export const BlogState = (props) => {
const host = "http://localhost:5000";
const blogInitial = [];
const [blog, setBlog] = useState(blogInitial);
let fetchAllNotes = async () => {
//API call
const response = await fetch(`${host}/api/notes/blog/`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const json = await response.json();
setBlog(json);
};
const searchFilter = (searchWord) => {
const searchTerm =
blog.filter((note) =>
note.description.toLowerCase().includes(searchWord)
) || ((note) => note.title.toLowerCase().includes(searchWord));
setBlog(searchTerm);
};
return (
<BlogContext.Provider value={{ blog, fetchAllNotes, fil, searchFilter }}>
{props.children}
</BlogContext.Provider>
);
};
Code from Navbar component
import React, { useContext, useState } from "react";
import { Link, useNavigate, useLocation } from "react-router-dom";
import { ThemeContext } from "../context/notes/ThemeContext";
import { BlogContext } from "../context/notes/BlogContext";
export const Navbar = () => {
const { searchFilter, blog } = useContext(BlogContext);
const [searchTerm, setSearchTerm] = useState(blog);
const onChange = (e) => {
if (e.target.value === "") {
window.location.reload(true);
} else {
const search = e.target.value.toLowerCase();
setSearchTerm(search);
searchFilter(searchTerm);
}
};
return (
<div>
<nav
<form className="d-flex mx-2">
<input
onChange={onChange}
className="form-control me-2"
type="search"
placeholder="Search"
aria-label="Search"
/>
<button className="btn btn-success mx-2" type="submit">Clear</button>
</form>
</nav>
</div>
);
};
Code from Blog component
import React, { useContext, useEffect } from "react";
import { ThemeContext } from "../context/notes/ThemeContext";
import { BlogContext } from "../context/notes/BlogContext";
import BlogItem from "./BlogItem";
import { FlexNavbar } from "./FlexNavbar";
const Blog = () => {
const { theme } = useContext(ThemeContext);
const { blog } = useContext(BlogContext);
return (
<>
<div
className={`container bg-${theme} text-${
theme === "dark" ? "light" : "dark"
}`}
>
<FlexNavbar className="" />
<div className="row">
{blog.map((notes) => {
return <BlogItem key={notes._id} note={notes} />;
})}
</div>
</div>
</>
);
};
export default Blog;

Props not getting Rendered in NextJs

I have a post page in blog model, which includes Detail and Comment Component. getStaticProps from the post page gets the data, and I am passing this data as props to the Detail Component. Yet the the Detail component is not getting rendered.
Here's the post page:
import BlogDetail from "../../components/BlogDetail/BlogDetail"
import { CSSTransition } from 'react-transition-group'
import AuthContextProvider from "../../context/AuthContext"
import Cookie from "../../components/Cookie"
import { ThemeContext } from "../../context/ThemeContext"
import { useContext } from "react"
import '#fortawesome/fontawesome-svg-core/styles.css'
import dynamic from 'next/dynamic'
const Navbar = dynamic(() => import("../../components/Navbar"), { ssr: false })
import axios from "axios"
import { baseURL } from "../../functions/baseUrl"
export const getStaticPaths = async () => {
const res = await axios.get(baseURL + "/post/");
const info = await res.data
const paths = info.map(datum => {
return {
params: { slug: datum.slug }
}
})
return {
paths,
fallback: false
}
}
export const getStaticProps = async (context) => {
const slug = context.params.slug;
const res = await axios.get(baseURL + '/post/' + slug + "/");
const data = await res.data;
return {
props: { datum: data }
}
}
const Blog = (datum) => {
const [dark] = useContext(ThemeContext)
return (
<div className={`main ${dark}`}>
<AuthContextProvider>
<Navbar />
<CSSTransition
in={true}
classNames="pagetransitions"
timeout={100}
key={1}
appear={true}
unmountOnExit
>
<div>
<Cookie />
<BlogDetail title={datum.title} datum={datum} />
</div>
</CSSTransition>
</AuthContextProvider>
</div>
)
}
export default Blog;
And my BlogDetail model:
import React, { useState } from 'react';
import styles from "../../styles/blogDetail.module.css"
import timeformatter from "../../functions/timeFormatter"
import Tags from "../Tags";
import Head from "next/head"
import dynamic from 'next/dynamic'
const Favourite = dynamic(() => import("./Favourite"), { ssr: false })
const Comment = dynamic(() => import("./Comment"), { ssr: false })
function BlogDetail(props) {
const data = useState(props.datum)
function createMarkup() {
return { __html: data.text };
}
return (
<div className={styles.blog_text_container}>
<div>
<h1>{data.title}</h1>
<Tags tag={data.tags} />
{data.image_head === null ? (
<span></span>
) : (
<div className={styles.article_detail_image}>
<img className={styles.blog_header_image_detail} alt={data.alt_image_text} src={data.image_head}></img>
</div>
)}
<br></br>
<div dangerouslySetInnerHTML={createMarkup()} />
<br></br>
<span>{data.comments}</span>
<br></br>
<span>Published: {timeformatter(data.created_at)}</span>
<br></br><br></br>
<Favourite id={data.id} favcount={data.favcount} /><br></br>
<Comment id={data.id} /><br></br>
<br></br>
</div>
</div>
)
}
export default BlogDetail
I have tried passing my props individually like the titles, texts. But nodda. I have tried directly rendering the props without including state. Any help would appreciated.
In post page destructure dataum
...other code remains same
const Blog = ({datum}) => {
...other code remains same
In blogdetail page
...other code remains same
function BlogDetail({datum : data}) { // destructuring dataum and naming it as data since you have used data.* in your code
// you donot need useState here
...other code remains same
The issue is probably here
// some code
const Blog = (datum) => {
// some code
Make it to
const Blog = ({datum}) => {
// your code
As explained here
https://nextjs.org/docs/basic-features/data-fetching#simple-example

child/parent problem rendering simple bar chart in react,

"EDITED"
I'm trying to make a very minimalist bar chart.
It doesn't, render. It seems that the child component isn't rendering after the parent component's state has changed.
for example. if I make a change in my code in the MiniChart component, and save the changed through my IDE. the charts will render correctly. but as soon as navigate to another page in my app through my browser and come back to where the charts are, then they won't render.
Any help will be much appreciated.
Child component:
import React, {useState, useEffect} from 'react';
import axios from 'axios';
import {BarChart, Bar} from 'recharts';
const MiniChart = (props) => {
const [apiUrl] = useState("https://api.coingecko.com/api/v3/coins/"+props.id+"/market_chart?vs_currency=usd&days=30&interval=daily");
const [data, setData] = useState([]);
const [msg, setMsg] = useState([]);
const [r, setR] = useState([]);
// fetch data from api
useEffect(() => {
const fetchData = async () => {
if(parseInt(props.rank) < 5){
const result = await axios(apiUrl,);
setData(result.data.prices);
} else {setMsg("TEST : not loaded");}
}
setR(data.map(elem => ({ 'val': elem[1]})));
fetchData();
return()=>{
}
}, [apiUrl, props.rank]);
return (
<div>
<BarChart width={150} height={40} data={r}>
<Bar dataKey='val' fill="green" />
</BarChart>
</div>
)
}
export default MiniChart
Parent component:
import React, { useState} from 'react'
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faStar } from "#fortawesome/free-solid-svg-icons";
import { Link, useLocation } from 'react-router-dom';
import Cookies from 'universal-cookie';
import MiniChart from './MiniChart';
const Crypto = (props) => {
const location = useLocation();
const [starColor, setStarColor] = useState(props.defaultStarCol);
const cookies = new Cookies();
const getFavs = cookies.getAll();
// toggle color, re-render, remove or add to cookies
const handleFavToggle = (e) => {
if(starColor === '#ebc934'){
setStarColor('lightgrey');
cookies.remove(props.id, props.id, { path: '/' });
if(location.pathname === '/favorites'){
function refreshPage() {
window.location.reload(false);
}
refreshPage();
}
} else {
setStarColor('#ebc934');
cookies.set(props.id, props.id, { path: '/' });
//console.log(cookies.getAll());
}
}
return (
<div>
<li>
<div className="lidiv">
{props.id in getFavs? //ADD IF LOGGED IN !
<p className="pml"><FontAwesomeIcon style={{color:'#ebc934'}} onClick={handleFavToggle} className="star" icon={faStar}/></p>
: <p className="pml"><FontAwesomeIcon style={{color:'lightgrey'}} onClick={handleFavToggle} className="star" icon={faStar}/></p>}
<p className="pml">{props.rank}</p>
<img className="iconimg" src={props.img} alt=""/>
<p className="pxl coinName"><Link to="/crypto" style={{display: 'block'}}>{props.coin}</Link></p>
<p className="pml">{props.tag}</p>
<p className="pml4">{props.price}</p>
<p className="pml" style={{color: (props.oneday).charAt(0)==='-' ? 'red': 'green'}}>{props.oneday}%</p>
<p className="pxl daycash" style={{color: (props.oneday).charAt(0)==='-' ? 'red': 'green'}}>{props.onedaycurr} </p>
<p className="pxl-4">{props.mcap}M</p>
<MiniChart className="pxl" id={props.id} rank={props.rank}></MiniChart>
</div>
</li>
</div>
)
}
export default Crypto;

Resources