SearchBar Assistance-React - reactjs

So I am trying to set a searchbar that has a drop down menu that gives you options on what you want to search for=and then let the user input. I have a list of inspections that I want to base that on.
For example I want to search inspections that only have issues with pests, so the user selects the pest option for search and then inputs-hive beetles
so the dropdown should have an option of searching by: pests, notes, actions taken, and so forth, my table has 8 values that I want to specific each search.
I have am using React for this and my database is in django, any suggestions on how to tackle this? The research I found is not really helpful.
My InpsectionSearch
import {React, useState} from "react";
const SearchInspections = (inspections) => {
const [searchField, setSearchField] = useState("")
const [searchResults, setSearchResults] = useState([])
function handleChange(event){
event.preventDefault()
const filteredInspections = inspections.filter((inspections) => {
return (
inspections.eggs().includes(searchField.toLowerCase()) ||
inspections.larvae().includes(searchField.toLowerCase()) ||
inspections.sealed_brood().includes(searchField.toLowerCase()) ||
inspections.covered_bees().includes(searchField.toLowerCase()) ||
inspections.nectar_honey().includes(searchField.toLowerCase()) ||
inspections.pollen().includes(searchField.toLowerCase()) ||
inspections.pest_spotted.toLowerCase().includes(searchField.toLowerCase()) ||
inspections.pest_action.toLowerCase().includes(searchField.toLowerCase()) ||
inspections.notes_concerns.toLowerCase().includes(searchField.toLowerCase())
)
})
setSearchResults(filteredInspections)
if(searchField.length > 0){
inspections(searchResults)
}
}
return (
<form onSubmit={handleChange}>
<h3>Search For Your Item</h3>
<input type="search" placeholder='Search Song' value={searchField} onChange={event => setSearchField(event.target.value)}/>
<button type='submit'>Click twice to search</button>
</form>
);
}
export default SearchInspections;
my Inspection Page where the searchbar will be
import { React, useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import axios from "axios";
import useAuth from "../../hooks/useAuth";
import DisplayInspections from "../../components/DisplayInspections/DisplayInspections";
import InspectionTracker from "../../components/InspectionTracker/InspectionTracker";
import SearchInspections from "../../components/InspectionSearch/InspectionSearch"
const InspectionPage = (props) => {
const [user, token] = useAuth();
const [inspections, setInspections] = useState([]);
const { id } = useParams();
useEffect(() => {
fetchInspections();
}, [token]);
const fetchInspections = async () => {
try {
let response = await axios.get(
`http://127.0.0.1:8000/api/inspections/all/${id}`,
{
headers: {
Authorization: "Bearer " + token,
},
}
);
setInspections(response.data);
} catch (error) {
console.log(error.response.data);
}
};
function applyFilter(category, userInput){
if(category === 'All' || userInput ===''){
console.log('working!')
fetchInspections();
}
else
{
let newInspections = inspections.filter(function(element){
if(element[category] === userInput){
return true;
}
})
setInspections(newInspections);
}
}
return (
<div className="container">
<InspectionTracker inspections={inspections} />
<SearchInspections inspections={inspections} />
<DisplayInspections
inspections={inspections}
setSelectedHive={props.setSelectedHive}
setSelectedInspection={props.setSelectedInspection}
/>
</div>
);
};
export default InspectionPage;

Related

How to use useSearchParams Hook with React Router v6

I am trying to implement a search parameter functionality to my React image search app. And, I have learned that I need to (can) use the useSearchParams Hook, but I am not sure how to make these changes.
So, basically I want the URL to be something like localhost:3000/input&page=1, meaning that whatever comes after the slash is going to be the input value and key/value pair for page numbers.
As you can see in the app.js, I have these 3 main Routes and the Home Route (renders Main.js) is the one I am mainly working on. Also, Main.js renders Header.js (renders form and others).
I am thinking that I should create a new Route in the app.js but I am not sure what to do.
import './App.css';
import Home from './components/pages/Home';
import Favorites from './components/pages/Favorites';
import Error from './components/pages/Error';
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { SkeletonTheme } from 'react-loading-skeleton';
import { useDarkMode } from './components/Navbar';
function App() {
const darkMode = useDarkMode(state => state.darkMode)
let style
if (darkMode === 'light') {
style = 'wrapper'
} else {
style = 'wrapper-dark'
}
return (
<div className={style}>
<SkeletonTheme baseColor="#808080" highlightColor="#b1b1b1">
<BrowserRouter>
<Routes>
<Route path='/' element={<Home />} />
<Route path='favorites' element={<Favorites />} />
<Route path='*' element={<Error />} />
</Routes>
</BrowserRouter>
</SkeletonTheme>
</div>
);
}
export default App;
import React from 'react'
import Header from './Header'
import Image from './Image'
import { useState, useEffect, useRef } from 'react'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faTriangleExclamation } from '#fortawesome/free-solid-svg-icons'
// import InfiniteScroll from 'react-infinite-scroll-component'
function Main() {
const [input, setInput] = useState('')
const [allImages, setAllImages] = useState([])
// const [totalResults, setTotalResults] = useState(null)
const [isVisible, setIsVisible] = useState(false)
const [error, setError] = useState(null)
const [showError, setShowError] = useState(false)
const [fadeOut, setFadeOut] = useState(false)
const [page, setPage] = useState(1)
const paginationRef = useRef(false)
// get
useEffect(() => {
if (localStorage.getItem('input')) {
setInput(JSON.parse(localStorage.getItem('input')))
}
if (localStorage.getItem('allImages')) {
setAllImages(JSON.parse(localStorage.getItem('allImages')))
// setTotalResults(JSON.parse(localStorage.getItem('totalResults')))
setIsVisible(JSON.parse(localStorage.getItem('isVisible')))
setPage(JSON.parse(localStorage.getItem('page')))
paginationRef.current = true
}
}, [])
// set
//* dryer?
useEffect(() => {
localStorage.setItem('input', JSON.stringify(input))
}, [input])
useEffect(() => {
localStorage.setItem('allImages', JSON.stringify(allImages))
}, [allImages])
// useEffect(() => {
// localStorage.setItem('totalResults', JSON.stringify(totalResults))
// }, [totalResults])
useEffect(() => {
localStorage.setItem('isVisible', JSON.stringify(isVisible))
}, [isVisible])
function handleChange(event) {
setInput(event.target.value)
}
// display nothing by default
// display image-list when user press search button
// function handleSubmit(event) {
// event.preventDefault()
// // interpolate input state and .env variable to API
// fetch(`https://api.unsplash.com/search/photos?query=${input}&client_id=${process.env.REACT_APP_UNSPLASH_API_KEY}`)
// .then(res => res.json())
// .then(data => setAllImages(data.results))
// }
async function fetchImages() {
try {
const res = await fetch(`https://api.unsplash.com/search/photos?&page=${page}&per_page=30&query=${input}&client_id=${process.env.REACT_APP_UNSPLASH_API_KEY}`)
const data = await res.json()
if (data.total !== 0) {
setAllImages(data.results)
// setTotalResults(data.total)
setIsVisible(true)
}
} catch(error) {
setError(error)
}
}
const handleSubmit = async (event) => {
event.preventDefault();
fetchImages()
setPage(1)
paginationRef.current = true
}
// error
useEffect(() => {
if (error) {
setShowError(true)
setTimeout(() => {
setFadeOut(true)
setTimeout(() => {
setShowError(false)
}, 1000)
}, 5000)
}
}, [error])
// total results
// let results
// if (totalResults >= 10000) {
// results = 'Total Results: ' + totalResults + '+'
// } else if (totalResults > 0) {
// results = 'Total Results: ' + totalResults
// } else if (totalResults === 0) {
// results = 'Nothing Found'
// }
// pagination
useEffect(() => {
if (paginationRef.current) {
fetchImages()
}
localStorage.setItem('page', JSON.stringify(page))
}, [page])
function handlePrev() {
setPage(prevState => prevState - 1)
fetchImages()
}
function handleNext() {
setPage(prevState => prevState + 1)
fetchImages()
}
return (
<main>
<Header
input={input}
handleChange={handleChange}
handleSubmit={handleSubmit}
/>
{showError && <div className={`network-error ${fadeOut ? 'fade-out' : ''}`}>
<i><FontAwesomeIcon icon={faTriangleExclamation} /></i>
<div className='network-error--message'>
<h5>Network Error</h5>
<p>Please check your Internet connection and try again</p>
</div>
</div>}
{/* <p className='main--results'>{results}</p> */}
<div className='main--image-list mt-5 pb-5'>
{allImages.map(el => (
<Image
key={el.id}
// do need spread operator below for img's src to work in Image.js
{...el}
el={el}
/>
))}
</div>
{isVisible && <div className='main--pagination'>
<button disabled={page === 1} onClick={handlePrev}>
Prev
</button>
<h5 className='main--pagination--h5'>{page}</h5>
<button onClick={handleNext}>
Next
</button>
</div>}
</main>
)
}
export default Main
import React from 'react'
import Navbar from './Navbar'
function Header(props) {
return (
<div className='header'>
<Navbar />
<h2 className='header--heading text-center text-light'>Find Images</h2>
<div className='header--form'>
<form onSubmit={props.handleSubmit}>
<input
className='header--form--input'
autoComplete='off'
type='text'
placeholder='Search'
onChange={props.handleChange}
name='input'
value={props.input}
/>
</form>
</div>
</div>
)
}
export default Header
If you are just wanting to initialize the page state to the page queryParam the the following could work. If uses the useSearchParams to access the queryString and return a constructed URLSearchParams object which can then access individual query params. Pass the "page" query param as the initial page state value.
const [searchParams] = useSearchParams();
const [page, setPage] = useState(Number(searchParams.get("page")) || 1);
In all likelihood though you'll not want competing "sources of truth" for what the current page is. If you want the URL queryString to be the source of truth then remove the page state and just read/update the "page` query parameter directly.
Example:
function Main() {
const [searchParams, setSearchParams] = useSearchParams();
...
const page = Number(searchParams.get("page"));
// get
useEffect(() => {
...
if (localStorage.getItem('allImages')) {
...
setSearchParams(params => {
params.set("page", JSON.parse(localStorage.getItem('page')) || 1);
return params;
});
...
}
}, []);
...
const handleSubmit = async (event) => {
event.preventDefault();
...
setSearchParams(params => {
params.set("page", 1);
return params;
});
...
}
...
// pagination
useEffect(() => {
if (paginationRef.current) {
fetchImages();
}
localStorage.setItem('page', JSON.stringify(page));
}, [page])
function handlePrev() {
setSearchParams(params => {
params.set("page", Math.max(1, page - 1));
return params;
});
...
}
function handleNext() {
setSearchParams(params => {
params.set("page", page + 1);
return params;
});
...
}
return (
...
);
}

Stripe : Could not find Elements context error in nextjs

I am using stripe following package in nextjs 12
import { loadStripe } from "#stripe/stripe-js";
import { Elements } from "#stripe/react-stripe-js";
It is working well in developement mode but while production build it is throwing this error
Error: Could not find Elements context; You need to wrap the part of your app that calls useStripe() in an <Elements> provider.
I am using Elements as a parent container and Checkoutform inside Elements as a child
import React from 'react'
import { useAppContext } from '#/context/AppContext';
import { loadStripe } from "#stripe/stripe-js";
import { Elements } from "#stripe/react-stripe-js";
import Checkoutform from './Checkoutform';
import AppLoader from '#/components/common/Loader';
const Payment = () => {
const { state } = useAppContext();
const { user, planType } = state;
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_KEY);
const options = {
clientSecret: planType.orderData.client_secret,
appearance: {
theme: 'stripe',
}
};
return (
options.clientSecret && options.clientSecret.length > 0 ? (
<Elements stripe={stripePromise} options={options} >
<Checkoutform userData={user} planType={planType} />
</Elements >
) : (
<AppLoader />
)
)
}
export default Payment
Checkoutform.js
import React, { useEffect, useState } from "react";
import {
PaymentElement,
useStripe,
useElements
} from "#stripe/react-stripe-js";
import AppLoader from "#/components/common/Loader";
import { toast } from "react-toastify";
import { baseURL } from "#/constants/utils/universal";
export default function Checkoutform(props) {
const { userData, planType } = props;
const stripe = useStripe();
const elements = useElements();
const [message, setMessage] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [isInitialized, setIsInitialized] = useState(true);
const redirectUrl = baseURL + "/pricing/payment/success";
useEffect(() => {
if (typeof window === "undefined" && !stripe) {
return;
}
const clientSecret = new URLSearchParams(window.location.search).get(
"payment_intent_client_secret"
);
if (!clientSecret) {
return;
}
stripe.retrievePaymentIntent(clientSecret).then(({ paymentIntent }) => {
switch (paymentIntent.status) {
case "succeeded":
toast.success("Thankyou!, Payment Successful, please check your email for your receipt");
setMessage("Payment succeeded!");
break;
case "processing":
setMessage("Your payment is processing.");
break;
case "requires_payment_method":
setMessage("Your payment was not successful, please try again.");
break;
default:
setMessage("Something went wrong.");
break;
}
});
}, [stripe]);
useEffect(() => {
setTimeout(() => {
if (typeof window !== "undefined") {
setIsInitialized(false);
}
}, 1000);
}, [])
const handleSubmit = async (e) => {
e.preventDefault();
if (!stripe || !elements) {
return;
}
setIsLoading(true);
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: redirectUrl,
},
});
if (error.type === "card_error" || error.type === "validation_error") {
toast.error("Oops! some error occurred, please try again");
setMessage(error.message);
} else {
toast.error("Oops! some error occurred, please try again");
setMessage("An unexpected error occured.");
}
setIsLoading(false);
};
return (
<div className='container my-5 pt-5'>
<div className='row'>
<div className='col-md-6 col-12 mx-auto'>
<div className='card shadow-sm p-5'>
{
!isInitialized ?
<form id="payment-form" onSubmit={handleSubmit}>
<PaymentElement id="payment-element" className="mb-5" />
<div className="mt-3 d-grid">
<button disabled={isLoading || !stripe || !elements} id="submit" className="btn app-cta">
<span id="button-text">
{isLoading ? "processing please wait..." : "Pay now"}
</span>
</button>
</div>
{/* Show any error or success messages */}
{message && <div id="payment-message" className='my-2 small fw-light text-danger'>{message}</div>}
</form>
:
<AppLoader />
}
</div>
</div>
</div>
</div>
);
}
please help me what i am doing wrong or what i have to add for production build
Jonathan Steele is right if you are using checkoutform.js as a component then make sure you kept it inside components folder which should be outside pages folder. Because if it is inside pages folder then during build nextjs will trying to pre-render it by considering it as a page which will give you this error

Component not rerendering after axios Get (React)

I'm trying to render List of items of my DB using React.Context.
All my request work pretty well.
when i console log my states first I get an empty array and then array with the data that I need but my component is not updating. I have to go to another page an then go back to this page to get the data. I don't really understand why... here are my files..
ArticlesContext.js :
import React, { useState, createContext, useEffect } from 'react';
import axios from 'axios'
export const ArticlesContext = createContext();
export function ArticlesProvider(props) {
const [articles, setArticles] = useState([]);
const [user, setUser] =useState(0)
async function getArticles () {
await axios.get(`/api/publicItem`)
.then(res => {
setArticles(res.data);
})
}
useEffect( () => {
getArticles()
}, [user])
console.log(articles);
return (
<ArticlesContext.Provider value={[articles, setArticles]}>
{props.children}
</ArticlesContext.Provider>
);
}
Inventaire.js :
import React, { useContext, useEffect, useState } from 'react';
import './Inventaire.css';
import { ArticlesContext } from '../../../context/ArticlesContext';
import DeleteAlert from './Delete/Delete';
import Modify from './Modify/Modify';
import Filter from './Filter/Filter';
import axios from 'axios'
import Crud from '../../Elements/Articles/Crud/Crud';
import List from './List/List';
export default function Inventaire() {
const [articles, setArticles] = useContext(ArticlesContext);
const [filter, setFilter] = useState(articles)
console.log(articles);
//list for Inputs
const cat = articles.map(a => a.category.toLowerCase());
const categoryFilter = ([...new Set(cat)]);
const gender = articles.map(a => a.gender.toLowerCase());
const genderFilter = ([...new Set(gender)]);
//Event Listenner
//Uncheck All checkboxes
function UncheckAll() {
const el = document.querySelectorAll("input.checkboxFilter");
console.log(el);
for (var i = 0; i < el.length; i++) {
var check = el[i];
if (!check.disabled) {
check.checked = false;
}
}
}
//SearchBar
const searchChange = (e) => {
e.preventDefault();
const stuff = articles.filter((i) => {
return i.name.toLowerCase().match(e.target.value.toLowerCase())
})
setFilter(stuff)
UncheckAll(true)
}
const Types = (e) => {
if (e.target.checked === true) {
const stuff = filter.filter((i) => {
return i.category.toLowerCase().match(e.target.value.toLowerCase())
})
setFilter(stuff)
console.log(articles);
} else if (e.target.checked === false) {
setFilter(articles)
}
}
const Gender = (e) => {
if (e.target.checked === true) {
const stuff = filter.filter((i) => {
console.log(i.category, e.target.value);
return i.gender.toLowerCase().match(e.target.value.toLowerCase())
})
setFilter(stuff)
} else if (e.target.checked === false) {
setFilter(articles)
}
}
return (
<div className="inventaireContainer">
<input type="text" placeholder="Recherche un Article" onChange={searchChange} />
<div className="inventaireMenu">
<Crud />
<Filter
filter={Types}
categorys={categoryFilter}
genre={genderFilter}
target={Gender}
/>
</div>
<List filter={filter} articles={articles}/>
</div>
)
}
List.js :
import React from 'react';
import DeleteAlert from '../Delete/Delete';
import Modify from '../Modify/Modify';
export default function List({ filter, articles }) {
return (
<div>
{filter.map((details, i) => {
return (
<div className="inventaireBlock" >
<div className="inventaireGrid">
<div className="inventaireItemImg">
<img src={details.image} alt="ItemImg" />
</div>
<h2>{details.name}</h2>
<h3>{details.category}</h3>
<h3>{details.gender}</h3>
<div>
<p>S :{details.sizes[0].s}</p>
<p>M :{details.sizes[0].m}</p>
<p>L :{details.sizes[0].l}</p>
<p>XL :{details.sizes[0].xl}</p>
</div>
<h2> Prix: {details.price}</h2>
<div className="modify">
<Modify details={details._id} />
</div>
<div className="delete" >
<DeleteAlert details={details._id} articles={articles} />
</div>
</div>
</div>
)
})}
</div>
)
}
Thanks for your time

React Hooks Forms: defaultValues not working as expected

I'm using hooks forms to fetch articles based on user's region. It can be changed any time, with select input, to change articles location.
import React, { useState, useEffect } from 'react';
import useForm from 'react-hook-form';
import { getRegions } from '../../services/regions';
import s from './style.module.scss';
function UpdateRegion({ putRegion, region }) {
const [regionsList, setRegionsList] = useState(null);
const [isLoading, setIsLoading] = useState(null);
const {
register, handleSubmit, watch, setValue,
} = useForm({
defaultValues: {
id: region !== null && region.id,
},
});
const { id } = watch();
const onSubmit = (data) => putRegion(regionsList.find((r) => (r.id === data.id)));
console.log('WATCH', id);
console.log('userRegionId', region !== null && region.id);
useEffect(() => {
register({ name: 'id' });
}, [register]);
const regions = async () => {
try {
setIsLoading(true);
const data = await getRegions();
setRegionsList(data);
setIsLoading(false);
} catch (error) {
setIsLoading(false);
console.log(error);
}
};
useEffect(() => {
if (regionsList === null && !isLoading) {
regions();
}
}, []);
useEffect(() => {
if (region !== null) {
setValue('id', region.id || '');
}
}, [region]);
return (
<form className={s.container}>
<select
name="id"
ref={register}
onChange={handleSubmit(onSubmit)}
className={s.container__select}
>
{regionsList !== null && (
regionsList.map((region) => (
<option
value={region.id}
key={region.id}
className={s.container__select__option}
>
{region.name}
</option>
))
)}
</select>
</form>
);
}
export default UpdateRegion;
DefaultValue should be all the time equal to region.id, and it works only if I refresh the page. If I change the page, and come back (without refreshing), my defaultValue is equal to the first option from select.
Why this is happening ?
EDIT
I resolved my issue by removing hooks forms:
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import Loading from 'Components/Loading';
import classNames from 'classnames';
import { getRegions } from '../../services/regions';
import s from './style.module.scss';
function UpdateRegion({ putRegion, region, className }) {
const [regionsList, setRegionsList] = useState(null);
const [isLoading, setIsLoading] = useState(null);
const regions = async () => {
try {
setIsLoading(true);
const data = await getRegions();
setRegionsList(data);
setIsLoading(false);
} catch (error) {
setIsLoading(false);
}
};
useEffect(() => {
if (regionsList === null && !isLoading) {
regions();
}
}, []); // eslint-disable-line
function changeRegion(e) {
putRegion(regionsList.find((r) => (r.id === e.target.value)));
}
if (region !== null) {
return (
<form className={classNames(s.container, className)}>
<select
name="id"
onChange={(e) => changeRegion(e)}
className={s.container__select}
value={region !== null && region.id}
>
{regionsList !== null && (
regionsList.map((r) => (
<option
value={r.id}
key={r.id}
className={s.container__select__option}
>
{r.name}
</option>
))
)}
</select>
</form>
);
} return <Loading small />;
}
UpdateRegion.propTypes = {
putRegion: PropTypes.func.isRequired,
region: PropTypes.object.isRequired,
className: PropTypes.string,
};
UpdateRegion.defaultProps = {
className: '',
};
export default UpdateRegion;
I think you have a wrong opinion about the defaultValue property. Let's try to explain why. First, when your hook calls for the first time your default value that has been passed from outside will be used later and it won't be changed any more. So, using the useEffect you will be able to observe and update the actual region id in your component.

React Router props.match and props.history.push are undefined while using useContext

I have moved my project to Codesandbox for better assistance with my question. Here is the link to the project.
I have two components SearchForm and AnimeDetails that receive API calls from my context component AnimeContext. The form is meant to display the searched Anime that was requested and AnimeDetails is supposed to display the details for the selected Anime.
I'm using props.match.params to get the id of the anime within the TopAnime component using <Link to={} /> and props.history.push to redirect to a new page once the form is submitted.
When I attempt to click on an Anime card to get the details, I receive
props.match is undefined
When I submit the form, I see the searched anime appear, but then I receive
props.history is undefined
I'm assuming this is React Router issue and that I am not setting something up correctly.
Here's what I have attempted so far and nothing has worked:
Using Redirect
Using the useHistory hook
Wrapping AnimeProvider with withRouter
In short, I cannot search for any titles and I cannot click on any Anime title on the HomePage to get it's details without getting undefined for props.match and props.history.
SearchForm component
import React, { useContext } from 'react';
import { withRouter } from 'react-router-dom'
import styled from 'styled-components'
import AnimeCard from './AnimeCard/AnimeCard';
import { AnimeContext } from '../store/AnimeContext'
const SearchForm = () => {
const { dataItems, animeSearched, handleSubmit } = useContext(AnimeContext)
return (
<div>
<Form onSubmit={handleSubmit}>
<Input
type="text"
name="anime"
placeholder="Enter title"
// ref={value => myValue = value}
/>
<FormButton type='submit'>Search</FormButton>
</ Form>
{animeSearched
?
<AnimeCard />
: null}
</div>
)
}
export default withRouter(SearchForm)
AnimeDetails component
import React, { useContext, useEffect } from "react";
import styled from "styled-components";
import { AnimeContext } from "../store/AnimeContext";
const AnimeDetails = () => {
const { fetching, anime, fetchAnimeDetails } = useContext(AnimeContext);
useEffect(() => {
fetchAnimeDetails();
});
return (
<>
{fetching && "Fetching..."}
{anime && (
<AnimeDetailsWrapper>
<AnimeDetailsContainer>
<Poster src={anime.image_url} />
{/* Details */}
<Details>
<Title>{anime.title}</Title>
<TitleJpn>{anime.title_japanese}</TitleJpn>
<Score>{anime.score || "N/A"}</Score>
{/* If no score then display N/A */}
<SongList>
<h3>Opening Themes</h3>
{anime.opening_themes // Make sure data is fully loaded before component renders
? anime.opening_themes.map((song, index) => (
<li key={index}>{song}</li>
))
: null}
</SongList>
</Details>
{/* Info Bar */}
<InfoBar>
{
<li>
Epiosdes: <span className="info-span">{anime.episodes}</span>
</li>
}
{
<li>
Duration: <span className="info-span">{anime.duration}</span>
</li>
}
{
<li>
<a
href={anime.trailer_url}
rel="external noopener noreferrer"
target="_blank"
>
View Trailer
</a>
</li>
}
</InfoBar>
{/* Synopsis */}
<Synopsis>{anime.synopsis}</Synopsis>
</AnimeDetailsContainer>
</AnimeDetailsWrapper>
)}
</>
);
};
export default AnimeDetails;
AnimeContext component
import React, { useState, useEffect, createContext } from 'react'
const AnimeContext = createContext()
const API = "https://api.jikan.moe/v3"
const AnimeProvider = (props) => {
const urls = [
`${API}/top/anime/1/airing`,
`${API}/top/anime/1/tv`,
`${API}/top/anime/1/upcoming`,
]
// State for top Anime
const [topTv, setTopTv] = useState([])
const [topAiring, setTopAiring] = useState([])
const [topUpcoming, setTopUpcoming] = useState([])
// State for Anime details
const [animeReq, setAnimeReq] = useState({
fetching: false,
anime: []
})
// State for Anime search form
const [dataItems, setDataItems] = useState([])
const [animeSearched, setAnimeSearched] = useState(false)
// Fetch top Anime
const fetchTopAnime = async () => {
return Promise.all(
urls.map(async url => {
return await fetch(url); // fetch data from urls
})
)
.then((responses) => Promise.all(responses.map(resp => resp.json())) // turn data into JSON
.then(data => {
const topTvFiltered = data[0].top.filter(item => item.rank <= 5) // filter out top 6
const topAiringFiltered = data[1].top.filter(item => item.rank <= 5)
const topUpcomingFiltered = data[2].top.filter(item => item.rank <= 5)
setTopTv(topTvFiltered)
setTopAiring(topAiringFiltered)
setTopUpcoming(topUpcomingFiltered)
console.log(data)
})
)
.catch(err => console.log("There was an error:" + err))
}
useEffect(() => {
fetchTopAnime()
}, [])
// Fetch Anime details
const fetchAnimeDetails = async () => {
setAnimeReq({ fetching: true })
const response = await fetch(`${API}/${props.match.params.animeId}`)
const data = await response.json()
console.log(data);
setAnimeReq({ fetching: false, anime: data }) // set initial state to hold data from our API call
}
const { fetching, anime } = animeReq;
// Fetch searched Anime
async function handleSubmit(e) {
e.preventDefault()
const animeQuery = e.target.elements.anime.value
const response = await fetch(`${API}/search/anime?q=${animeQuery}&page=1`)
const animeData = await response.json()
setDataItems(animeData.results)
setAnimeSearched(!animeSearched)
props.history.push('/dashboard')
}
return (
<AnimeContext.Provider value={{
topTv,
setTopTv,
topAiring,
setTopAiring,
topUpcoming,
setTopUpcoming,
dataItems,
setDataItems,
animeSearched,
setAnimeSearched,
fetching,
anime,
fetchTopAnime,
fetchAnimeDetails,
handleSubmit
}}>
{props.children}
</AnimeContext.Provider>
)
}
export { AnimeProvider, AnimeContext }
You should do this
import { useLocation, useNavigate } from 'react-router-dom';
//You should navigate
const navigate = useNavigate();
navigate('/app/example', { state: { message: "hello" } });
//You can receive
const location = useLocation();
console.log("location data", location.state);

Resources