rick and morty infinity loop on query - reactjs

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

Related

How to store and access data from array [product] from context?

I have a context which contains a fetch() method used to retieve list of products from a server.I made this context so that I could reuse the fetched array values every webpage I might need.But I am unable to do so as it gives me an error in console.
this is the code for context
import React, { createContext, useState, useEffect } from 'react'
export const ProductContext = createContext()
const ProductContextProvider = (props) => {
const [product, setProduct] = useState([]);
const fetchData = () => {
fetch(`http://localhost:8080/product`)
.then((response) => response.json())
.then((actualData) => {
setProduct(actualData)
console.log(product);
})
};
useEffect(() => {
fetchData();
}, [])
return (
<ProductContext.Provider
value={{ product }}>
{props.children}
</ProductContext.Provider>
)
}
export default ProductContextProvider
and this is the error I am getting in console
enter image description here
I have done this too in index.js
enter image description here
and this is one page I want to call the product[]
import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { useContext } from 'react'
import ProductContext from '../context/ProductContext';
function Product() {
const { product } = useContext(ProductContext)
console.log(product);
return (
<div className="products-row ">
{
product.map((data, num) => {
return (
<div className="product" key={num}>
<div className="card">
<a target="_blank" href="/design" >
<img src={data.thumbnail} alt={data.name} style={{ width: "100%" }} ></img>
</a>
<h1>{data.name}</h1>
<p className="price">${data.price}</p>
</div>
</div>
)
})
}
</div>
);
}
export default Product;
I believe it's an import issue. You probably meant to use the following:
import { ProductContext } from '../context/ProductContext';
Right now, your ProductContext is actually ProuductContextProvider, which is the default export as per your code.

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;

Pass value from parent to child component with React

I trying to display the rating of a query in my React App. But I'm not sure if I understand how to handle the state.
This is my query component:
import React, { Component, useRef, useState, useEffect } from 'react';
import { render } from 'react-dom';
import InputSearchLandlord from './search'
import './style.css'
import SimpleRating from '../components/star_display'
import ReactStars from 'react-rating-stars-component'
import './style.css'
const HandleSearch = () => {
const [ratingValue, setRating] = useState(0)
const [name, searcName] = useState("")
const nameForm = useRef(null)
const average = arr => arr.reduce( ( p, c ) => p + c, 0 ) / arr.length;
const ratings = []
const displayComment = async() => {
try {
const form = nameForm.current
const name = form['name'].value
searchName(name)
const response = await fetch(`localhost`)
const jsonData = await response.json()
getComments(jsonData)
comments.forEach(e => {
console.log(e.rating)
ratings.push(e.rating)
})
const rating = average(ratings) //Avg of all rating associated with the search
console.log(rating) //Should be pass to Rating component
setRating(rating)
} catch (error) {
console.log(error.message)
}
}
return(
<div className="container">
<div className="form-group">
<h1 className="text-center mt-5">SEARCH</h1>
<form ref={nameForm} className="mt-5">
<InputSearch name={'name'}/>
<div className="d-flex justify-content-center">
<button type="submit" className="d-flex btn btn-primary" onClick={displayComment}>Search</button>
</div>
</form>
<div>
<div className='container'>
<h1>{name}</h1>
<SimpleRating data={ratingValue}
/>
</div>
<div className='container'>
{comments.map(comment => (
<div className="commentSection">
<a>
{comment.problem}
</a><br/>
<a>
Posted on : {comment.date}
</a>
</div>
))}
</div>
</div>
</div>
</div>
)
}
export default HandleSearch;
And this is my Rating component:
import React, { useState } from 'react';
import { render } from 'react-dom';
import ReactStars from 'react-rating-stars-component'
import './style.css'
import HandleSearch from '../pages/handleSearch'
export default function SimpleRating(rating) {
const [ratingValue, setRating] = useState(0)
const options = {
value: ratingValue, //Should use the value from the Search component
a11y: true,
isHalf: true,
edit: false,
};
console.log(options.value)
if (options.value == 0) return null //if rating value = 0 doesn't display the component
return (
<div className="starComponent">
<ReactStars {...options}/>
</div>
);
}
So I trying to pass the value computed in the Search component to the Rating component. Before any query is made with the Search component, the value should be 0 and hidden.
What am I missing ?
Its to do with your props. In your parent component you create a prop called data so in your rating component you need to extract that value from props
// HandleSearch Component
<SimpleRating data={ratingValue}
export default function SimpleRating(props) {
const { data } = props
// You can also just say props.data
... rest of your component
}
Currently you are actually defining the props in your SimpleRating component but you are calling them rating (it doesn't actually matter what you call it but commonly its called props) and that is an object that contains all of the props that you pass into that component.

Front-End Filtering React JS

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

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