I am trying to implement a stock dashboard app which uses the CoinGecko API. At the beginning, I created the following action.js,
export const ALL_COINS = "ALL_COINS";
export const ALL_CATEGORIES = "ALL_CATEGORIES";
export const ALL_EXCHANGES = "ALL_EXCHANGES";
export const ALL_ASSETPLATFORMS = "ALL_ASSETPLATFORMS";
import {
fetchAllCoins,
fetchAllCategories,
fetchAllExchanges,
fetchAllAssetPlatforms,
} from "../services/coinService";
import {
ALL_COINS,
ALL_CATEGORIES,
ALL_EXCHANGES,
ALL_ASSETPLATFORMS,
} from "./actionTypes";
export const fetchData = () => async (dispatch) => {
const response = await fetchAllCoins();
dispatch({
type: ALL_COINS,
payload: response.data,
});
};
export const fetchCategories = () => async (dispatch) => {
const response = await fetchAllCategories();
dispatch({
type: ALL_CATEGORIES,
payload: response.data,
});
};
export const fetchExchanges = () => async (dispatch) => {
const response = await fetchAllExchanges();
dispatch({
type: ALL_EXCHANGES,
payload: response.data,
});
};
export const fetchAssetPlatforms = () => async (dispatch) => {
const response = await fetchAllAssetPlatforms();
dispatch({
type: ALL_ASSETPLATFORMS,
payload: response.data,
});
};
And the following reducers:
import {
ALL_COINS,
ALL_CATEGORIES,
ALL_EXCHANGES,
ALL_ASSETPLATFORMS,
} from "./actionTypes";
export const initialState = {
allCoins: [],
categories: [],
exchanges: [],
assetPlatforms: [],
};
eslint-disable-next-line no-lone-blocks
export const fetchReducer = (state = [], action) => {
switch (action.type) {
case ALL_COINS:
return action.payload;
case ALL_CATEGORIES:
return action.payload;
case ALL_EXCHANGES:
return action.payload;
case ALL_ASSETPLATFORMS:
return action.payload;
default:
return state;
}
};
import { configureStore } from "@reduxjs/toolkit";
import { fetchReducer } from "./redusers";
import { combineReducers } from "@reduxjs/toolkit";
const rootReducer = combineReducers({
fetch: fetchReducer,
});
export default configureStore({
reducer: rootReducer,
});
I also use separate coinService
import axios from "axios";
export const fetchAllCoins = () => {
return axios.get(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd",
{
headers: {
"Content-Type":
"application/x-www-form-urlencoded; charset=UTF-8;application/json",
},
}
);
};
export const fetchAllCategories = () => {
return axios.get("https://api.coingecko.com/api/v3/coins/categories/list", {
headers: {
"Content-Type":
"application/x-www-form-urlencoded; charset=UTF-8;application/json",
},
});
};
export const fetchAllExchanges = () => {
return axios.get("https://api.coingecko.com/api/v3/exchanges", {
headers: {
"Content-Type":
"application/x-www-form-urlencoded; charset=UTF-8;application/json",
},
});
};
export const fetchAllAssetPlatforms = () => {
return axios.get("https://api.coingecko.com/api/v3/asset_platforms", {
headers: {
"Content-Type":
"application/x-www-form-urlencoded; charset=UTF-8;application/json",
},
});
};
All of this code above I use in different components, for example:
export default function CustomizedTables() {
const dispatch = useDispatch();
const currency = useSelector((state) => state.fetch);
const input = useInput();
const [page, setPage] = useState(0);
const [searchTerm, setSearchTerm] = useState("");
const [rowsPerPage, setRowsPerPage] = useState(5);
When I used the code which I provided above, I received responses from the API and my UI rendered data. However, I received the same data, so I decided to rewrite my reducer.js like this:
import {
ALL_COINS,
ALL_CATEGORIES,
ALL_EXCHANGES,
ALL_ASSETPLATFORMS,
} from "./actionTypes";
export const initialState = {
allCoins: [],
categories: [],
exchanges: [],
assetPlatforms: [],
};
// TODO: finish redusers
export const fetchReducer = (state = initialState, action) => {
switch (action.type) {
case ALL_COINS:
return {
...state,
allCoins: action.payload,
};
case ALL_CATEGORIES:
return {
...state,
categories: action.payload,
};
case ALL_EXCHANGES:
return {
...state,
exchanges: action.payload,
};
case ALL_ASSETPLATFORMS:
return {
...state,
assetPlatforms: action.payload,
};
default:
return state;
}
};
When I use first variant of the reducer the I receive data from API, but when I use second reducer variant I get a few errors, one of the the following:
CustomizedTables.jsx:47 Uncaught TypeError: currency.filter is not a function
CustomizedTables.jsx:
export default function CustomizedTables() {
const dispatch = useDispatch();
const currency = useSelector((state) => state.fetch);
const input = useInput();
const [page, setPage] = useState(0);
const [searchTerm, setSearchTerm] = useState("");
const [rowsPerPage, setRowsPerPage] = useState(5);
const filteredCryptoCurrency = currency.filter((coin) =>
coin.name.toLowerCase().includes(searchTerm.toLowerCase())
);
const paginatedCryptoCurrency = filteredCryptoCurrency.slice(
page * rowsPerPage,
page * rowsPerPage + rowsPerPage
);
const newLocal = "rgb(123, 182, 77)";
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleInputChange = (value) => {
setSearchTerm(value);
};
const handleRowsPerPageChange = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
useEffect(() => {
dispatch(fetchData());
}, [dispatch]);
return (
<TableContainer component={Paper}>
<InputSearch style={{}} onInputChange={handleInputChange} />
<Table sx={{ minWidth: 500 }} aria-label="customized table">
<TableHead>
<TableRow>
<TableCell
sx={{ backgroundColor: "rgb(31, 37, 61)", color: "white" }}
>
Image
</TableCell>
<TableCell
sx={{ backgroundColor: "rgb(31, 37, 61)", color: "white" }}
>
Name
</TableCell>
<TableCell
sx={{ backgroundColor: "rgb(31, 37, 61)", color: "white" }}
>
Symbol
</TableCell>
<TableCell
sx={{ backgroundColor: "rgb(31, 37, 61)", color: "white" }}
>
Price
</TableCell>
<TableCell
sx={{ backgroundColor: "rgb(31, 37, 61)", color: "white" }}
>
24h
</TableCell>
<TableCell
sx={{ backgroundColor: "rgb(31, 37, 61)", color: "white" }}
>
Volume
</TableCell>
<TableCell
sx={{ backgroundColor: "rgb(31, 37, 61)", color: "white" }}
>
Market Cap
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{setSearchTerm //isSearchFieldEmpty
? paginatedCryptoCurrency.map((coin) => (
<StyledTableRow key={coin.image}>
<StyledTableCell component="th" scope="row">
<img
src={coin.image}
alt=""
style={{ height: "2rem", width: "2rem" }}
/>
</StyledTableCell>
<StyledTableCell>{coin.name}</StyledTableCell>
<StyledTableCell>{coin.symbol}</StyledTableCell>
<StyledTableCell>${coin.current_price}</StyledTableCell>
<StyledTableCell>
<span style={{ color: newLocal }}>
{coin.price_change_percentage_24h}%
</span>
</StyledTableCell>
<StyledTableCell>${coin.total_volume}</StyledTableCell>
<StyledTableCell>${coin.market_cap}</StyledTableCell>
</StyledTableRow>
))
: paginatedCryptoCurrency
.filter((coin) =>
coin.name.toLowerCase().includes(input.value.toLowerCase())
)
.map((coin) => (
<StyledTableRow key={coin.image}>
<StyledTableCell component="th" scope="row">
<img
src={coin.image}
alt=""
style={{ height: "2rem", width: "2rem" }}
/>
</StyledTableCell>
<StyledTableCell>{coin.name}</StyledTableCell>
<StyledTableCell>{coin.symbol}</StyledTableCell>
<StyledTableCell>${coin.current_price}</StyledTableCell>
<StyledTableCell>
<span style={{ color: newLocal }}>
{coin.price_change_percentage_24h}
</span>
</StyledTableCell>
<StyledTableCell>
${coin.total_volume.toLocaleString()}
</StyledTableCell>
<StyledTableCell>
${coin.market_cap.toLocaleString()}
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
<TablePagination
rowsPerPageOptions={[]}
colSpan={3}
count={filteredCryptoCurrency.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleRowsPerPageChange}
ActionsComponent={PaginationActions}
sx={{
display: "flex",
justifyContent: "center",
backgroundColor: "rgb(31, 37, 61)",
color: "#fff",
}}
/>
</TableContainer>
);
}
But for now, this doesn't work at all. What exactly am I doing wrong? I understand that my mistake is obvious, but I would be grateful for any clues on what direction I should move in order to resolve this issue.