Reputation: 420
I'm working on a Cryptocurrency project. When I'm on the home page and search for an item in the search box and click on the item I go to the second page and everything is ok. But on the second page When I search for an item and click on it URL changed but the page doesn't change and the data on-page is the same as before. I made a gif image you can understand better.
this issue is related to two components searchbarLayout(search box) and coinsLayout(second page as I mentioned above) I added both components code.
I'm using <Link />
for passing id and going to the second page.
<Link to={`coins/${name}`} state={{ id }}>
{name}
</Link>
And I'm using useLocation()
for getting Id and useParams()
for getting the name of the coin.
const location = useLocation();
const { name } = useParams();
const [id, setId] = useState(location.state?.id);
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { Avatar } from "antd";
import { AutoComplete } from "antd";
import { getRequest } from "utils/api/index";
import { RoundSearchBar } from "./styles.js";
import "./styles.css";
const renderTitle = (title) => <span>{title}</span>;
const renderItem = (name, id, icon) => ({
value: name,
label: (
<div
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<span>
<Link to={`coins/${name}`} state={{ id }}>
{name}
{console.log("search id: ", id, "name coin : ", name)}
</Link>
</span>
<span>
<Avatar src={icon} />
</span>
</div>
),
});
export const Complete = () => {
const [querySuggest, setQuerySuggest] = useState("");
const [coinSearched, setCoinSearched] = useState([]);
const handleSearch = (value) => {
setQuerySuggest(value);
};
useEffect(() => {
const getCoins = async () => {
await getRequest(`v2/search-suggestions?query=${querySuggest}`)
.then((response) => {
console.log(querySuggest);
const { coins } = response.data.data;
const selectedCoins = coins.map((c) => {
return {
id: c.uuid,
icon: c.iconUrl,
name: c.name,
};
});
setCoinSearched(selectedCoins);
})
.catch((error) => console.log(error));
};
getCoins();
}, [querySuggest]);
const options = [
{
label: renderTitle("Coins"),
// options: [renderItem("AntDesign"), renderItem("AntDesign UI")],
options: coinSearched.map((coin) => {
return renderItem(coin.name, coin.id, coin.icon);
}),
},
];
return (
<AutoComplete
dropdownClassName="certain-category-search-dropdown"
dropdownMatchSelectWidth={500}
style={{ width: 250 }}
options={options}
onChange={handleSearch}
>
<RoundSearchBar size="large" placeholder="Search..." />
</AutoComplete>
);
};
export default Complete;
import React, { useState, useEffect } from "react";
import { useLocation, useParams } from "react-router-dom";
import { Skeleton, Card, Avatar, Row, Col, Divider, Radio, Button } from "antd";
import { Line } from "@ant-design/plots";
import { getRequest } from "utils/api";
import {
justUnixTimeStampConvert,
convertTimeStampToDate,
} from "utils/convertTimestampToDate";
import "./styles.css";
import commaSeparator from "utils/table/commaSeparator";
import NotFound from "screens/errors";
const { Meta } = Card;
export const CoinsLayout = () => {
const [coinData, setCoinData] = useState({});
const [totalData, setTotalData] = useState("");
const [circulatingData, setCirculatingData] = useState("");
const [chartData, setChartData] = useState([]);
const [timePeriodHistory, setTimePeriodHistory] = useState("1y");
const [coinFounded, setCoinFounded] = useState(true);
const location = useLocation();
const { name } = useParams();
const [id, setId] = useState(location.state?.id);
const [loading, setLoading] = useState(true);
const handleTimePeriodHistory = (time) => {
setTimePeriodHistory(time);
};
const getCoin = async () => {
getRequest(`v2/coin/${id}?referenceCurrencyUuid=yhjMzLPhuIDl`)
.then((response) => {
const { coin } = response.data.data;
setTotalData(coin.supply.total);
setCirculatingData(coin.supply.circulating);
setCoinData(coin);
setLoading(false);
})
.catch((error) => {
console.log("error", error);
});
};
const getCoins = async () => {
getRequest("v2/coins")
.then((response) => {
const { coins } = response.data.data;
console.log(coins);
const coinFound = coins.find((coin) => coin.name === name);
if (!coinFound) {
setCoinFounded(false);
setLoading(false);
} else {
setId(coinFound.uuid);
}
setLoading(false);
})
.catch((error) => {
console.log(error);
});
};
useEffect(() => {
if ((typeof id !== undefined) & (id !== null) & (id !== "")) {
setCoinFounded(true);
getCoin();
} else {
getCoins();
}
}, [id]);
useEffect(() => {
if ((typeof id !== "undefined") & (id !== null) & (id !== "")) {
setCoinFounded(true);
const getHistoryPrice = async () => {
await getRequest(
`v2/coin/${id}/history?timePeriod=${timePeriodHistory}`
)
.then((response) => {
const { data } = response.data;
const result = convertTimeStampToDate(data.history);
setChartData(result);
})
.catch((error) => console.log(error));
};
getHistoryPrice();
} else {
getCoins();
}
}, [timePeriodHistory, id]);
const config = {
data: chartData,
padding: "auto",
xField: "date",
yField: "price",
xAxis: {
tickCount: 5,
},
smooth: true,
};
if (coinFounded === false) {
console.log("not found coind");
return <NotFound />;
} else {
return (
<div className="site-card-wrapper">
<br />
<br />
<br />
<br />
<br />
<Row key={id}>
<Col xs={{ span: 5, offset: 1 }} lg={{ span: 6, offset: 2 }}>
<Skeleton loading={loading} avatar active>
<Card
style={{
margin: "5px",
borderRadius: "10px",
overflow: "hidden",
}}
title="Price Information"
bordered={true}
>
<Meta
title={coinData.name + ` Price (${coinData.symbol})`}
avatar={<Avatar src={coinData.iconUrl} />}
description={<h2>{commaSeparator(coinData.price)}</h2>}
></Meta>
</Card>
</Skeleton>
</Col>
<Col xs={{ span: 5, offset: 1 }} lg={{ span: 6 }}>
<Skeleton loading={loading} avatar active>
<Card
style={{
margin: "5px",
borderRadius: "10px",
overflow: "hidden",
}}
title="Market Cap & Ciruculating Supply"
bordered={true}
>
<Row>
<Col span={8}>
<h3>Market Cap</h3>
<h3>{commaSeparator(coinData.marketCap)} </h3>
</Col>
<Col span={8} offset={4}>
<h3>Circulating Supply</h3>
<h3>{commaSeparator(circulatingData)} </h3>
</Col>
</Row>
</Card>
</Skeleton>
</Col>
<Col xs={{ span: 5, offset: 1 }} lg={{ span: 6 }}>
<Skeleton loading={loading} avatar active>
<Card
style={{
margin: "5px",
borderRadius: "10px",
overflow: "hidden",
}}
title="Volume & Total Supply"
bordered={true}
>
<Row>
<Col span={8}>
<h3>Volume 24h</h3>
<h3>{commaSeparator(coinData["24hVolume"])} </h3>
</Col>
<Col span={8} offset={4}>
<h3>Total Supply</h3>
<h3>{commaSeparator(totalData)} </h3>
</Col>
</Row>
</Card>
</Skeleton>
</Col>
</Row>
<br />
<br />
<br />
<Divider orientation="center">
<h2>Charts Data</h2>
</Divider>
<Row>
<Col span={6} offset={2}>
<div>
<h1>{coinData.name + ` Price (USD)`}</h1>
</div>
</Col>
<Col span={6} offset={5}>
<Radio.Group defaultValue="1y" buttonStyle="solid">
<Radio.Button
onClick={() => handleTimePeriodHistory("24h")}
value="24h"
>
24h
</Radio.Button>
<Radio.Button
onClick={() => handleTimePeriodHistory("7d")}
value="7d"
>
7d
</Radio.Button>
<Radio.Button
onClick={() => handleTimePeriodHistory("30d")}
value="30d"
>
30d
</Radio.Button>
<Radio.Button
onClick={() => handleTimePeriodHistory("1y")}
value="1y"
>
1y
</Radio.Button>
</Radio.Group>
</Col>
</Row>
<Row>
<Col span={16}>
<Card
style={{
marginTop: "15px",
marginLeft: "100px",
borderRadius: "10px",
overflow: "hidden",
marginBottom: "40px",
}}
span={8}
offset={2}
>
<Line {...config} />
</Card>
</Col>
<Col span={6}>
<Card
style={{
margin: "5px",
borderRadius: "10px",
overflow: "hidden",
marginTop: "15px",
}}
title="Coin Info"
bordered={true}
>
<div>
<Button type="primary" size="large">
WebSite
</Button>
<Button
style={{ marginLeft: "10px", fontWeight: "bold" }}
orientation="center"
type="primary"
ghost
>
{coinData.websiteUrl}
</Button>
</div>
<Divider></Divider>
<div>
<Button type="primary" size="large">
{coinData.name} Rank
</Button>
<Button
style={{ marginLeft: "10px", fontWeight: "bold" }}
orientation="center"
type="primary"
ghost
>
{coinData.rank}
</Button>
</div>
<Divider></Divider>
<div>
<div>
<Button type="primary" size="large">
AllTimeHigh
</Button>
<Button
style={{ marginLeft: "10px", fontWeight: "bold" }}
orientation="center"
type="primary"
ghost
>
{commaSeparator(coinData.allTimeHigh?.price)}
</Button>
</div>
<br />
<div>
<Button type="primary" size="large">
Date
</Button>
<Button
style={{ marginLeft: "10px", fontWeight: "bold" }}
type="primary"
danger
ghost
>
{justUnixTimeStampConvert(coinData.allTimeHigh?.timestamp)}
</Button>
</div>
</div>
<Divider></Divider>
</Card>
</Col>
</Row>
</div>
);
}
};
export default CoinsLayout;
Upvotes: 0
Views: 1678
Reputation: 2388
Your component is not re-rendering when the query props are changing. Here's what's happening:
id
state as location.state?.id
. This gets the correct coin id.id
state is changed, so the useEffect
hook is triggered which calls the getCoin()
method.id
.getCoin()
doesn't get called.You need to modify the useEffect hook to couple it with the location params instead. Then you can set the new id state and update the data. Here's one way to do it:
useEffect(() => {
const newId = location.state?.id;
if (!newId) {
getCoins();
}
setId(newId);
getCoin();
}, [location]);
PS. a few bugs spotted:
&
is not a logical operator, you should be using &&
instead.coinFounded
→ coinFound
PPS. You're using async functions but within them you're using .then
callbacks. You can remove the async
keyword, or keep it and rewrite the calls using await
.
Upvotes: 2