I have a simple routing system using hash router in my react app. I have a MainDisplay that Links to the route /assets/id. I also have a component called Trending that Links to the same route /assets/id. The link in MainDisplay works perfectly, but the link in Trending does not. The link for Trending changes the URL when I click on it but doesn't send me to the proper page until I refresh.
The trending component is being rendered by another component called CryptoInfo, which holds information based on the id I got from MainDisplay. Can someone help me understand why my routing isn't working in the Trending component?
// MainDisplay.js
<Link key={index} to={`/assets/${id}`}>
<span>{name}</span>
<span>${current_price}</span>
</Link>
// CryptoInfo
import React, { useState, useEffect } from 'react'
import { useParams } from "react-router-dom"
import '../../styles/CryptoInfo.css'
import MainNav from '../nav/MainNav'
import Chart from './Chart'
import Description from './Description'
import Footer from '../main/Footer'
import Exchanges from './Exchanges'
import News from './News'
import Trending from './Trending'
export default function CryptoInfo() {
const [currentData, setCurrentData] = useState([])
const [image, setImage] = useState("")
const [currentPrice, setCurrentPrice] = useState("")
const [marketCap, setMarketCap] = useState("")
const [marketData, setMarketData] = useState([])
const [days, setDays] = useState(1)
const [usd, setUsd] = useState('')
const [currency, setCurrency] = useState('')
const { id } = useParams()
const api = `https://api.coingecko.com/api/v3/coins/${id}`
async function fetchApi() {
const data = await fetch(api)
const response = await data.json()
setCurrentData(response)
setImage(response.image.small)
setCurrentPrice(response.market_data.current_price.usd)
setMarketData(response.market_data)
setMarketCap(response.market_data.market_cap.usd)
}
useEffect(() => {
fetchApi()
}, [days])
const currentDataDisplay = (
<>
<div className='top-div'>
<div >
<span className='market-rank'>Rank: {market_cap_rank}</span>
</div>
<div className='name-div'>
<img className='coin-image' src={image} alt="" />
<span className='crypto-name'>{name}</span>
<span className="c-info-symbol">{SYMBOL}</span>
</div>
</div>
{/* <div className='todays-range'>
<div>
<progress className='progress-bar' min="0" max="0.14" value="0.1"></progress>
</div>
<div>
<span>{todayLow}</span>
<span>
-
</span>
<span> {todayHigh}</span>
</div>
</div> */}
<div className='price-div'>
<span className='current-price'>${current_price}</span>
<span className='price-change-24' style={{ color: priceChange24h > 0 ? 'green' : 'red' }} > {priceChange24h} %</span>
</div>
</>
)
return (
<>
<MainNav />
<div className="crypto-info-container">
<div className="crypto-info-top-container">
<div>
{currentDataDisplay}
</div>
<Chart />
</div>
<div className='crypto-info-bottom-container'>
<div className='des-container'>
<Description symbol={SYMBOL} />
<Exchanges />
</div>
<News />
</div>
<Trending />
</div>
<Footer />
</>
)
}
// Trending.js
import React, { useEffect, useState } from 'react'
import { Paper } from '#mui/material'
import { Link } from 'react-router-dom'
export default function Trending() {
const [trendingData, setTrendingData] = useState([])
const [trendingStats, setTrendingStats] = useState([])
const api = 'https://api.coingecko.com/api/v3/search/trending'
useEffect(() => {
fetchData()
}, [])
const fetchData = async () => {
const data = await fetch(api)
const response = await data.json()
setTrendingData(response.coins)
}
const fetchMarketData = async () => {
const data = await fetch(fetchAll)
const response = await data.json()
setTrendingStats(response)
}
let coinsArray = []
trendingData && trendingData.forEach((item) => {
const { id } = item.item
coinsArray.push(id + '%2C%20')
})
const allIds = coinsArray.join('').replace(',', '').slice(0, -6)
const fetchAll = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=${allIds}&order=market_cap_desc&per_page=100&page=1&sparkline=true`
useEffect(() => {
fetchMarketData()
}, [allIds, trendingStats])
const trendingDivv1 = (
trendingStats && trendingStats.slice(0, 4).map((coin, index) => {
const { name, price_change_percentage_24h, image, id, current_price } = coin
const sparkline = coin.sparkline_in_7d.price
let borderColor
let changePercent = price_change_percentage_24h.toFixed(2)
return (
<Link key={index} to={`/assets/${id}`}>
<Paper className="trending-box" elevation={3}>
<div style={{ display: 'flex' }}>
<div><img style={{ borderRadius: '50%', width: '50px' }} src={image} alt="" /></div>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<span>{name}</span>
<span>${current_price}</span>
</div>
<span style={{ color: borderColor, marginLeft: 'auto' }}>{changePercent}%</span>
</div>
<div className='trending-chart'>
<Sparklines data={sparkline}>
<SparklinesLine color={borderColor} />
</Sparklines>
</div>
</Paper >
</Link>
)
})
const trendingDivv2 = (
trendingStats && trendingStats.slice(4, 8).map((coin, index) => {
const { name, price_change_percentage_24h, image, id, current_price } = coin
const sparkline = coin.sparkline_in_7d.price
let borderColor
let changePercent = price_change_percentage_24h.toFixed(2)
return (
<Link to={`/assets/${id}`}>
<Paper className="trending-box" elevation={3}>
<div style={{ display: 'flex' }}>
<div><img style={{ borderRadius: '50%', width: '50px', height: '50px' }} src={image} alt="" /></div>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<span> {name}</span>
<span>${current_price}</span>
</div>
<span style={{ color: borderColor, marginLeft: 'auto' }}>{changePercent}%</span>
</div>
<div className='trending-chart'>
<Sparklines data={sparkline}>
<SparklinesLine color={borderColor} />
</Sparklines>
</div>
</Paper >
</Link >
)
})
)
return (
<>
<div className='trending-div'>
<h1 style={{ margin: '20px 0' }}>Trending Coins 🔥</h1>
<div className='trending-coins-div'>
<div className='trending-flex-box'>
{trendingDivv1}
</div>
<div className='trending-flex-box'>
{trendingDivv2}
<a href={'/'}>
<div className="trending-box trending-image" >
<h2 style={{ color: 'white', fontWeight: '700', fontSize: '1.5em', verticalAlign: 'center' }}>See More Coins</h2>
</div >
</a>
</div>
</div>
</div>
</>
)
}
//App.js
<>
<Router>
<Switch>
<Route exact path="/" >
<div>
<MainNav />
<InfoNav />
<MainDisplay />
<Footer />
</div>
</Route>
<Route path="/assets/:id">
<CryptoInfo />
</Route>
<Route path="/categories">
<CategoryList />
</Route>
</Switch>
</Router>
</>
So it turns out the link in Trending is working. The issue is that the CryptoInfo component doesn't respond to the id route param updating to fetch any new data.
export default function CryptoInfo() {
...
const [days, setDays] = useState(1); // <-- (3) setDays never called, days never updates
...
const { id } = useParams(); // <-- (1) id param updates
const api = `https://api.coingecko.com/api/v3/coins/${id}`; // <-- (2) api value updated
async function fetchApi() {
...
}
useEffect(() => {
fetchApi(); // <-- (5) no refetch
}, [days]); // <-- (4) dependency doesn't update
...
return (....);
}
To resolve, move the api and fetchApi function into the useEffect and use the correct dependencies, just id in this case. You should also surround any asynchronous code in a try/catch to handle any rejected promises or any other thrown exceptions.
Example:
function CryptoInfo() {
...
const { id } = useParams();
useEffect(() => {
const api = `https://api.coingecko.com/api/v3/coins/${id}`;
async function fetchApi() {
try {
const data = await fetch(api);
const response = await data.json();
setCurrentData(response);
setImage(response.image.small);
setCurrentPrice(response.market_data.current_price.usd);
setMarketData(response.market_data);
setMarketCap(response.market_data.market_cap.usd);
} catch (error) {
// handle error
console.log(error);
}
}
fetchApi();
}, [id]);
...
return (...);
}
Related
I would appreciate to know why it gives this './undefined.jpg' before anything else and only AFTER that, renders all the actual paths.
import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
import style from './CarsListPage.module.scss';
//import cars from './car-content';
import CarsList from '../components/CarsList';
const CarsListPage = () => {
const [carsInfo, setCarsInfo] = useState([{}]);
useEffect(() => {
const loadCarsInfo = async () => {
const response = await axios.get('/api/cars');
const newCarsInfo = response.data;
setCarsInfo(newCarsInfo);
};
loadCarsInfo();
}, []);
return (
<div className={style.mainCotainer}>
<main className={style.main}>
<h1>Cars</h1>
<div className={style.container}>
{carsInfo.map((car) => (
<Link to={`/cars/${car.name}`} key={car.id}>
<div className={style.card} key={car.id}>
<h3>{car.name}</h3>
{/* {console.log(`../temp-img/${car.title}.jpg`)} */}
<p>{car.body_type}</p>
<p>{car.origin}</p>
<p>{car.year}</p>
<img
src={require(`../temp-img/${car.title}.jpg`)}
alt={car.name}
style={{ width: '200px' }}
/>
</div>
</Link>
))}
</div>
</main>
</div>
);
};
export default CarsListPage;
I've found couple solutions like wrapping everying into div and check whether value exists or not but i could not optimize it for my code.
Change the default state of carsInfo to [] otherwise you will map on an empty object until you get the data from the API:
const CarsListPage = () => {
const [carsInfo, setCarsInfo] = useState([]);
useEffect(() => {
const loadCarsInfo = async () => {
const response = await axios.get('/api/cars');
const newCarsInfo = response.data;
setCarsInfo(newCarsInfo);
};
loadCarsInfo();
}, []);
return (
<div className={style.mainCotainer}>
<main className={style.main}>
<h1>Cars</h1>
<div className={style.container}>
{carsInfo.length && carsInfo.map((car) => (
<Link to={`/cars/${car.name}`} key={car.id}>
<div className={style.card} key={car.id}>
<h3>{car.name}</h3>
{/* {console.log(`../temp-img/${car.title}.jpg`)} */}
<p>{car.body_type}</p>
<p>{car.origin}</p>
<p>{car.year}</p>
<img
src={require(`../temp-img/${car.title}.jpg`)}
alt={car.name}
style={{ width: '200px' }}
/>
</div>
</Link>
))}
</div>
</main>
</div>
);
};
I'm trying to add some simple fade animations to component which displays information about marvel character chosen in another component (characters list). When I use in my component (BasicCharacterInfo), it is being rendered multiple times and besides on first render it displays comics and then it rerenders instantly without comics.
Here is code:
import { Skeleton } from '#mui/material';
import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import Transition from 'react-transition-group/Transition';
import useMarvelService from '../../services/MarvelService';
import ErrorMessage from '../errorMessage/ErrorMessage';
import './charInfo.scss';
// Creating element with photo and description of character
function BasicCharacterInfo({
data: {
name,
description,
thumbnail,
urls: { charHomePage, charWikiPage },
comics,
},
}) {
const [readyToDisplay, setReadyToDisplay] = useState(() => false);
console.log('COMICS === ' + comics.length);
useEffect(() => setReadyToDisplay(true), []);
let descriptionToDisplay;
if (description) {
const splitDescription = description.split(' ');
if (splitDescription.length >= 124) {
descriptionToDisplay = splitDescription.splice(0, 124).join(' ');
}
descriptionToDisplay = description;
}
const transitionStyles = {
entering: { opacity: 1, transition: 'all 200ms ease' },
entered: { opacity: 1, transition: 'all 200ms ease' },
exiting: { opacity: 0, transition: 'all 200ms ease' },
exited: { opacity: 0, transition: 'all 200ms ease' },
};
return (
<Transition
in={readyToDisplay}
timeout={{ enter: 100, exit: 500 }}
mountOnEnter
unmountOnExit
>
{state => (
<div style={{ ...transitionStyles[state] }}>
<div className="char__basics">
<img src={thumbnail} alt={name} />
<div>
<div className="char__info-name">{name}</div>
<div className="char__btns">
<a href={charHomePage} className="button button__main">
<div className="inner">homepage</div>
</a>
<a href={charWikiPage} className="button button__secondary">
<div className="inner">wiki</div>
</a>
</div>
</div>
</div>
<div className="char__descr">
{descriptionToDisplay || 'No description for this character...'}
</div>
<div className="char__comics">Comics:</div>
<ul className="char__comics-list">
{comics.length
? comics.splice(0, 10).map(({ name, resourceURI }) => {
const comicId = resourceURI.split('/').at(-1);
return (
<li className="char__comics-item" key={comicId}>
<Link to={`/comic/${comicId}`}>{name}</Link>
</li>
);
})
: 'No comics found for this character'}
</ul>
</div>
)}
</Transition>
);
}
// Skeleton for loading
const CharacterInfoLoadingSkeleton = ({ initialTitle }) => {
const title = initialTitle ? (
<p
style={{
fontWeight: 'bold',
fontSize: '18px',
textAlign: 'center',
margin: '5px 0 10px',
}}
>
{initialTitle}
</p>
) : null;
return (
<>
{title}
<Skeleton variant="circular" width={80} height={80} animation="wave" />
<Skeleton
style={{ margin: '15px 0' }}
variant="rectangular"
height={150}
animation="wave"
/>
<Skeleton animation="wave" />
<Skeleton animation="wave" />
<Skeleton animation="wave" />
</>
);
};
// Main function component
const CharInfo = props => {
// State
const [chosenCharacter, setChosenCharacter] = useState(null);
const [initialTitle, setInitialTitle] = useState(
'Choose character from list on the left'
);
const { fetchSingleCharacter, loading, error } = useMarvelService();
// State managment
const onNewCharacterId = newCharacter =>
setChosenCharacter(() => newCharacter);
// Update character
const updateCharacterInfo = () => {
if (!props.characterId) {
return;
}
// Fetch clicked character, store it into state and stop loading
// also call onError function if error occures
return fetchSingleCharacter(props.characterId)
.then(response => {
setInitialTitle(null);
return onNewCharacterId(response);
})
.catch(e => {
throw e;
})
.finally();
};
// Updating character when component is mounted to display its info
// eslint-disable-next-line
useEffect(() => updateCharacterInfo, []);
// Updating character to display its info
useEffect(() => {
async function fetchData() {
return await updateCharacterInfo();
}
fetchData();
// eslint-disable-next-line
}, [props.characterId]);
const content =
!loading && !error && chosenCharacter ? (
<>
<BasicCharacterInfo data={chosenCharacter} />
</>
) : error ? (
<ErrorMessage />
) : (
<CharacterInfoLoadingSkeleton initialTitle={initialTitle} />
);
return <div className="char__info">{content}</div>;
};
export default CharInfo;
I am trying to add animations with react-transition-goup but its' behavior is not correct.
I am working on an ecommerce, where I am using material UI pagination component for implementing pagination. Here is new requirement arises. I need to add functionality in pagination: if user click on let's say respectively 3,7,11,13 if they click on browser back button they will go back to 11 then 7 then 3 and lastly 1. How do I do that?
I am using react, react router dom.
Here is pagination structure:
FYI, this is url and API structure:
URL and API structure
import React, { useEffect, useState } from "react";
import { useHistory, useParams } from "react-router-dom";
import ProductList from "../../components/common/ProductList/ProductList";
import {
GET_PRODUCTS_BY_BRAND,
GET_PRODUCTS_BY_CATEGORY,
GET_PRODUCTS_BY_SUBCATEGORY,
GET_PRODUCTS_BY_VENDOR,
} from "../../requests/HomePageApi";
import { Pagination } from "#material-ui/lab";
import "./ShopPage.scss";
const ShopPage = () => {
const { type, slug, subcategory } = useParams();
const [loading, setLoading] = useState(true);
// const [error, setError] = useState(false);
const [brands, setBrands] = useState([]);
const [colors, setColors] = useState([]);
const [sizes, setSizes] = useState([]);
const [products, setProducts] = useState(null);
const [filteredProducts, setFilteredProducts] = useState(null);
const [page, setPage] = React.useState(0);
const [count, setCount] = React.useState(1);
const [limit, setLimit] = React.useState(60);
const [total, setTotal] = React.useState(60);
const [sideFilter, setSideFilter] = useState(false);
const [vandor, setvandor] = useState({
vendorImg: "",
vendorName: "",
vendorSlug: "",
});
const [filter, setFilter] = useState({
// brands: "",
color: "",
size: "",
price: "",
});
const closeSideFilter = () => {
setSideFilter(false);
};
const getProducts = async (slug, qParams) => {
try {
let res;
if (type === "category") {
subcategory
? (res = await GET_PRODUCTS_BY_SUBCATEGORY(
slug,
subcategory,
qParams
))
: (res = await GET_PRODUCTS_BY_CATEGORY(slug, qParams));
}
if (type === "brand") res = await GET_PRODUCTS_BY_BRAND(slug, qParams);
if (type === "store") res = await GET_PRODUCTS_BY_VENDOR(slug, qParams);
if (res) setLoading(false);
if (res && res.products && res.products.length > 0) {
setProducts(res.products);
setFilteredProducts(res.products);
setTotal(res.total);
setCount(Math.ceil(res.total / limit));
if (type === "brand") {
setvandor({
vendorImg: `/assets/images/brand/${res.products[0].brand_logo}`,
vendorName: res.products[0].brand_name,
vendorSlug: res.products[0].brand_slug,
});
} else if (type === "store") {
setvandor({
vendorImg: `/assets/images/brand/${res.products[0].brand_logo}`,
vendorName: res.products[0].shop_name,
vendorSlug: res.products[0].vendorSlug,
});
}
if (res.colors) {
const uniqueColors = [...new Set(res.colors)];
setColors(uniqueColors);
}
if (res.sizes) {
const uniqueSizes = [...new Set(res.sizes)];
setSizes(uniqueSizes);
}
// if (res.brands) setBrands(res.brands);
}
} catch (error) {
console.log(error);
}
};
// console.log({ filteredProducts, filter, page, count, limit, total });
React.useMemo(() => {
let qParams = {
page: page,
limit: limit,
size: filter.size,
color: filter.color,
// brands: filter.brands,
price: filter.price.length ? `${filter.price[0]},${filter.price[1]}` : "",
};
if (slug) {
getProducts(slug, qParams);
}
}, [slug, page, limit, filter, count]);
React.useEffect(() => {
setPage(0);
}, [filter]);
const changeLimit = (limit) => {
setPage(0);
setLimit(limit);
};
const handleChange = (event, value) => {
// window.scrollTo({ top: 0, left: 0, behavior: "smooth" });
setPage(value - 1);
};
const slugTitle = (slug) => slug.split("-").join(" ");
return (
<FadeTransition>
{/* {loading && (
<div className="section-big-py-space ratio_asos py-5">
<div className="custom-container">
<Skeleton type="ShopPage" />
</div>
</div>
)} */}
{!loading && products === null && (
<div className="section-big-py-space ratio_asos py-5">
<div className="custom-container">
<h3 style={{ color: "#32375A", textAlign: "center" }}>
Sorry, No Product Found!
</h3>
</div>
</div>
)}
{products && (
<div className="title-slug-section">
<h2 class="title-slug">{slug && slugTitle(slug)}</h2>
</div>
)}
{products && (
<section className="section-big-py-space ratio_asos">
{/* {type !== "category" && (
<div className="merchant-page-header">
<div className="custom-container">
<div
className="shadow-sm bg-white rounded p-3 mb-5 d-flex align-items-center w-100"
style={{ minHeight: "132px" }}
>
<div className="row align-items-center w-100">
<div className="col-lg-6">
<div className="row align-items-center">
{vandor && vandor.vendorImg && (
<div className="col-auto">
<Image
src={vandor.vendorImg}
alt={vandor.vendorName}
className="img-fluid merchant-img"
/>
</div>
)}
<div className="col-auto mt-lg-0 mt-2">
<h3 className="mb-0"> {vandor.vendorName} </h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)} */}
<div className="collection-wrapper">
<div className="custom-container">
<div className="row">
<div className="col-sm-3 collection-filter category-page-side">
{/* <SidebarFilter
type={type}
brands={brands}
colors={colors}
sizes={sizes}
onChange={(data) => setFilter(data)}
/> */}
<InnerCategory />
{products && (
<RowSlider title="New Products" products={products} />
)}
</div>
<div className="collection-content col-lg-9">
<div className="page-main-content">
<div className="collection-product-wrapper">
<div className="row">
<div className="col-xl-12">
{/* <Button
variant='contained'
className='bg-dark text-light d-lg-none mb-3 mt-2 w-100'
onClick={() => setSideFilter(true)}
>
<span className='filter-btn '>
<i
className='fa fa-filter'
aria-hidden='true'
></i>
Filter
</span>
</Button> */}
</div>
</div>
<MainFilter
type={type}
// brands={brands}
colors={colors}
sizes={sizes}
page={page}
limit={limit}
onCountChange={(c) => changeLimit(c)}
onChange={(data) => setFilter(data)}
/>
{/* <TopFilter
onCountChange={(x) => changeLimit(x)}
total={total}
page={page}
limit={limit}
setSideFilter={setSideFilter}
/> */}
{filteredProducts && (
<ProductList products={filteredProducts} />
)}
{count > 1 && (
<div className="d-flex justify-content-center mt-4">
<Pagination
count={count}
page={page + 1}
onChange={handleChange}
shape="rounded"
/>
</div>
)}
</div>
</div>
</div>
</div>
</div>
</div>
</section>
)}
{!loading && products?.length === 0 && (
<div className="merchant-page-header">
<div className="custom-container pt-5">
<div
className="shadow-sm bg-white rounded p-3 mb-5 d-flex align-items-center justify-content-center w-100"
style={{ minHeight: "132px" }}
>
<h3 className="mb-0">No Products found!</h3>
</div>
</div>
</div>
)}
<Drawer
open={sideFilter}
className="add-to-cart"
onClose={() => setSideFilter(false)}
transitionDuration={400}
style={{ paddingLeft: "15px" }}
>
<SidebarFilter
onClose={closeSideFilter}
type={type}
// brands={brands}
colors={colors}
sizes={sizes}
onChange={(data) => setFilter(data)}
/>
</Drawer>
</FadeTransition>
);
};
export default ShopPage;
Usually you would like to have URL to represent selected page, so you could refresh the page and still be on the same page. Or share the exact page via copy-paste of URL. Especially on e-commerce sites. So I would recommend to sync selected page with URL.
While it's so common scenario, i have couple hooks for that. First of all - URL hook, which should work with your reactjs app setup.
https://www.npmjs.com/package/hook-use-url
and then couple more hooks to not worry about pagination details inside component:
usePage.js:
import useUrl from "hook-use-url";
export default function usePage() {
const url = useUrl();
const page = url.get({ variable: "page" })
? parseInt(url.get({ variable: "page" }), 10)
: 1;
const setPage = (value) => {
url.multipleActions({
setPairs: [{ variable: "page", value: value }],
});
};
return [page, setPage];
}
and usePerPage.js:
import useUrl from "hook-use-url";
export default function usePerPage() {
const url = useUrl();
const perPage = url.get({ variable: "per-page" })
? parseInt(url.get({ variable: "per-page" }), 10)
: 25;
const setPerPage = (value) => {
url.multipleActions({
setPairs: [
{ variable: "page", value: 1 },
{ variable: "per-page", value },
],
});
};
return [perPage, setPerPage];
}
Inside components you can use these like so:
(Take a note that which page is 1st depends on your backend API, in my case 1st page is always 1 and not 0, but mui.com component starts from 0 that's why there is -1 and +1).
function MyComp(){
const [page, setPage] = usePage();
const [perPage, setPerPage] = usePerPage();
// ....
return (
<TablePagination
count={totalRows}
page={page - 1}
onPageChange={(e, newPage) => {
setPage(newPage + 1);
}}
rowsPerPage={perPage}
onRowsPerPageChange={(e) => {
setPerPage(e.target.value);
}}
/>
)
}
i am tryin to route to localhost/detiled on click on the <li>
when i type in url localhost/detiled my <InnerDetail /> is loading i want the same when i click on the <li> tag
and also how can i access suggestion.id in <InnerDetail />
search.js
<Link to={{ pathname:"/detiled" }}
>
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
onClick={finddoctor(suggestion.id)}
>
{suggestion.firstname}
</li>
</Link>
in my path.js i have this
path.js
<Route path="/detiled">
<InnerDetail />
</Route>
import PersonPhoto from "../img/doctor.png";
import { useLocation } from "react-router-dom";
import axios from "axios";
import React, { useState, useEffect } from "react";
export default function Detail(props) {
const location = useLocation();
const [detail, setDetail] = useState(false);
//const data3 = location.state.data;
//const doctor_id = data3.id;
const inipath = window.location.pathname;
const path = inipath.split("/cdoctor/");
const [data3, setdata3] = useState([]);
console.log(path[1]);
useEffect(() => {
const config = {
headers: {
Authorization: `token ` + localStorage.getItem("token"),
},
};
//remove this date after setting up the admin pannel
axios
.get(
"doctor-filter/?id=" + path[1],
config
// config
)
.then((res) => {
console.log(res.data);
// setdata3(res.data);
});
});
return (
<>
{/* {detail ? (
<InnerDetail />
) : (
<> */}
<h4 style={{ textAlign: "left", marginLeft: "10px" }}>
Top Specialities <i className="fa fa-angle-right"></i> /doctor/Detail
</h4>
<hr
style={{
margin: "30px 10px",
background: "#fff",
height: "1px",
border: "none",
}}
/>
<div style={styles.wrapper}>
{/* begin header */}
<div style={styles.pheader}>
<div>
<div style={styles.pname}>
<strong style={styles.namealigner}>Dr {data3.firstname}</strong>
<br />
<strong style={styles.namealigner}> {data3.lastname}</strong>
</div>
<hr style={styles.hr} />
<div style={{ textAlign: "left", fontSize: "12px" }}>
<span>
{" "}
{data3.speciality} | {data3.experience} years of Experience
</span>
</div>
<hr style={styles.hr} />
</div>
<div>
<img style={{ height: "100px" }} src={PersonPhoto} alt="" />
</div>
</div>
{/* end header */}
{/* begin detail */}
<div style={styles.iflex}>
<div style={styles.innerflex}>
<i className="fa fa-graduation-cap"></i>
<strong> Education</strong>
<br />
<small> {data3.qualification}</small>
</div>
<div style={styles.innerflex}>
<i className="fa fa-map-marker"></i>
<strong> Location</strong>
<br />
<small>{data3.location}</small>
</div>
</div>
<div style={styles.iflex}>
<div style={styles.innerflex}>
<i className="fa fa-user"></i>
<strong> Registeration Number</strong>
<br />
<small>{data3.councilRegNo}</small>
</div>
<div style={styles.innerflex}>
<i className="fa fa-globe"></i>
<strong> Language</strong>
<br />
<small>English Malayalam</small>
</div>
</div>
{/* end detail */}
</div>
</>
// )}
// </>
);
}
this is the dashbord here serch is common in all pages
<Search />
<Specialities />
import React from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearch } from "#fortawesome/free-solid-svg-icons";
import { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import { useHistory } from "react-router-dom";
const initialState = {
idaddProducts: "",
};
const Searchclients = () => {
const history = useHistory();
const [showResults, setShowResults] = React.useState(true);
const [poName, pnName] = React.useState(initialState);
const [showSerch, setShowSerch] = React.useState([]);
const [detail, setDetail] = useState(false);
const [inputValue, setInputValue] = React.useState("");
const [filteredSuggestions, setFilteredSuggestions] = React.useState([]);
const [selectedSuggestion, setSelectedSuggestion] = React.useState(0);
const [displaySuggestions, setDisplaySuggestions] = React.useState(false);
function finddoctor(e) {
console.log(e);
setDetail(true);
}
const suggestions = [];
showSerch.forEach(function (data) {
suggestions.push(data);
});
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
//console.log(strung.substring(1, strung.length - 1));
// console.log(JSON.stringify(suggestions));
// var suggestions = suggestions.substring(1, suggestions.length - 1);
// newObj = suggestions;
//console.log(suggestions);
//setFilteredSuggestions({ ...poName, idAddProducts: idAddProducts });
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
// const filteredSuggestions = suggestions.filter((suggestion) =>
// suggestion.toString().toLowerCase().includes(value.toLowerCase())
// );
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
// console.log(props);
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
// console.log(suggestions);
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<Link to={`/detiled/${suggestion.id}`}> //this isthe link
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</Link>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("all-doctors-list/")
.then((res) => {
const data = res.data;
// pnName(data.data);
// var stringdata = data;
setShowSerch(data);
//console.log(stringdata);
});
// setShowSerch(data);
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
onClick={() => this.nextPath("/detiled")}
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
export default Searchclients;
You can add a route for detail view of the suggestion.
<Switch>
<Route path="/detailed" exact>
list suggestions component
</Route>
<Route path="/detailed/:id" exact}>
DetailComponent
</Route>
</Switch>
Then the link will become:
<Link to={`/detailed/${suggestion.id}`}>
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</Link>
And in DetailComponent you can get the id from route params.
import { useParams } from "react-router-dom";
...
const { id } = useParams(); // the id passed in url
I am having trouble loading the react-material-ui-carousel at the indexed image onClick.
Here is how far I got:
import ImageList from '#material-ui/core/ImageList';
import ImageListItem from '#material-ui/core/ImageListItem';
import HighlightOffIcon from '#material-ui/icons/HighlightOff';
import Carousel from 'react-material-ui-carousel';
export default function Dashboard() {
const [gallery, setGallery] = React.useState(true);
const [carousel, setCarousel] = React.useState(false);
const [slidePosition, setSlidePosition] = React.useState([]);
const images = [
{ id: 1, src: `${image1}` },
{ id: 2, src: `${image2}` },
{ id: 3, src: `${image3}` },
]
const showCarousel = (e) => {
setGallery(false);
setSlidePosition(e.target);
setCarousel(true);
const showGallery = () => {
setCarousel(false);
setGallery(true);
}
return(
<div>
{gallery && (
<div className="gallery">
<div className={classes.root}>
<ImageList rowHeight={160} className={classes.imageList} cols={3}>
{pics.map((image, index) => (
<ImageListItem key={index} cols={image.cols || 1} >
<img key={index} id={image.id} src={image.src} alt="" maxHeight="100px"
onClick={showCarousel}
/>
</ImageListItem>
))}
</ImageList>
</div>
</div>
)}
{carousel && (
<Carousel>
{images.map((image, index) => (
<div key={index} cols={image.cols || 1} style={{ display: "flex",
justifyContent: "center" }}>
<img key={index} id={slidePosition.id} src={slidePosition.src} alt=""
maxHeight="100px" onClick={showGallery}
/>
</div>
))}
</Carousel>
)
}
)
</div>
}
The current solution will show the expanded carousel view of the image from the ImageList, however, when it transitions, it will not show the rest of the images, but keep repeating the same image. Otherwise, without the [slidePosition, setSlidePosition] hook, the carousel would just start from image [0] of the array.