What is the correct way to use React-Redux when developing a stock dashboard?

44 Views Asked by At

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.

0

There are 0 best solutions below