Search code examples
javascriptreactjsfiltermappingrendering

TypeError undefined is not a function (near...map)


enter image description hereI'm trying to implement a simple filter section to filter results of an array that I'm mapping and when my function is finding the correct answer and is supposed to change the state, the results are not re-rendering even tho it's based on the same state. Maybe it comes from the way I'm mapping through the result, because my filter in only returning one match and should be displayed by the same mapping function that display all the results by default. It's really annoying because I know my filter function is returning the right answer but I can't get to make it showed on the page.

The whole site crashes. Please help !

import React, { useState, useEffect } from "react";
import { useAuth0 } from "@auth0/auth0-react";
import "../style/content.css";
import LoginButton from "./LoginButton";
import Modal from "./Modal";
var axios = require("axios").default;

const StockContent = () => {
  const { isAuthenticated } = useAuth0();
  const [datas, setDatas] = useState([]);
  const [selectFil, setSelectFil] = useState("");
  const [isOpen, setIsOpen] = useState(false);

  useEffect(() => {
    var options = {
      method: "GET",
      url: "https://run.mocky.io/v3/6daa5c5c-4c1b-4bce-8596-c604fa9f52dc",
      params: { modules: "defaultKeyStatistics,assetProfile" }
    };

    axios
      .request(options)
      .then(function (response) {
        var results = response.data.marketSummaryResponse.result;
        setDatas(results);
        console.log(results);
      })
      .catch(function (error) {
        console.error(error);
      });
  }, []);

  const handleChange = (e) => {
    debugger;
    setSelectFil(e.target.value);
  };

  const findMatch = (selected) => {
    debugger;
    const matchData = datas.find((data) => data.exchange === selected);
    setDatas(matchData);
  };

  return (
    <>
      {isAuthenticated ? (
        <>
          <div className="title">
            <h1>
              Market Summary <i class="fas fa-chart-pie"></i>
            </h1>
          </div>
          <div className="filter_sec">
            <h3>Search By Filter</h3>
            <br></br>
            <select name="exchange" id="stock" value={selectFil} onChange={handleChange}>
              {datas.map((data, i) => {
                return <option>{data.exchange}</option>;
              })}
            </select>
            <select name="market" id="mark">
              {datas.map((data, i) => {
                return <option value="market">{data.market}</option>;
              })}
            </select>
            <br />
            <button onClick={() => findMatch(selectFil)}>Filter</button>
          </div>
          <div className="card_content">
            {datas &&
              datas.length > 0 &&
              datas.map((data, i) => {
                return (
                  <div className="card_style" key={data}>
                    <div className="card_info">
                      <div className="logo_sn">
                        <h4 id="stock_sn">{data.shortName}</h4>
                        <div className="logo"></div>
                      </div>
                      <div className="card_info_det">
                        <h4>Exchange : {data.exchange}</h4>
                        <h4>Market : {data.market}</h4>
                        <h4>TimeZone : {data.exchangeTimezoneName}</h4>
                        <h4>Change Rate : {data.regularMarketChangePercent.fmt}</h4>
                        <h4>Market Price :{data.regularMarketPrice.fmt} $</h4>
                      </div>
                      <div>
                        <Modal
                          exchange={data.exchange}
                          exchangeDelay={data.exchangeDataDelayedBy}
                          exchangeMilisec={data.firstTradeDateMilliseconds}
                          marketState={data.marketState}
                          priceHint={data.priceHint}
                          quoteSrcN={data.quoteSourceName}
                          quoteType={data.quoteType}
                          regMarketChF={data.regularMarketChange.fmt}
                          regMarketChR={data.regularMarketChange.raw}
                          regMarketPrevCF={data.regularMarketPreviousClose.fmt}
                          regMarketPrevCR={data.regularMarketPreviousClose.raw}
                          regMarketTimeF={data.regularMarketTime.fmt}
                          regMarketTimeR={data.regularMarketTime.raw}
                          shortName={data.shortName}
                          srcInter={data.sourceInterval}
                          symbol={data.symbol}
                          tradeable={data.tradeable}
                          trigger={data.triggerable}
                          isOpen={isOpen}
                          onClose={(e) => setIsOpen(false)}
                        />
                      </div>
                    </div>
                  </div>
                );
              })}
          </div>
        </>
      ) : (
        <div className="not_logged_page">
          <h1>Welcome to Finan.Stock !</h1>
          <br />
          <h2>Invest Better</h2>
          <LoginButton />
        </div>
      )}
    </>
  );
};

export default StockContent;


Solution

  • The reason you're getting this error is because initially selectFil is an empty string (const [selectFil, setSelectFil] = useState("")), so if you don't change select's value, onChange isn't triggered and the result of findMatch() is undefined. You could fix this by setting a default value to selectFil after you fetch the data:

    axios
      .request(options)
      .then(function (response) {
        var results = response.data.marketSummaryResponse.result;
        setSelectFil(results[0].exchange); // <--------------------- HERE
        setDatas(results);
        console.log(results);
      })
    

    So hopefully you won't get the error TypeError undefined is not a function (near...map) anymore but this actually still won't work, because you datas.find() is not returning an array ; I would recomment using datas.filter() instead, like so:

    const matchData = datas.filter((data) => data.exchange === selected)
    

    But there is still a problem: when datas is updated inside your findMatch function (setDatas(matchData)), it will render the component again and the <select> options will also be "filtered" as you can see below:

    The select options are also filtered

    What I would do is create a filteredDatas variable (const [filteredDatas, setFilteredDatas] = useState([])) and stored filtered datas inside it instead :

    const findMatch = (selected) => {
        const matchData = datas.filter((data) => data.exchange === selected);
        setFilteredDatas(matchData);
    };
    

    Then you would map through filteredDatas and it should work:

    {filteredDatas.length > 0 &&
      filteredDatas.map((data, i) => {
        return (
          <div style={{ border: "1px solid gray" }}>
            <h4>Exchange : {data.exchange}</h4>
            <h4>Market : {data.market}</h4>
            <h4>TimeZone : {data.exchangeTimezoneName}</h4>
            <h4>Change Rate : {data.regularMarketChangePercent.fmt}</h4>
            <h4>Market Price :{data.regularMarketPrice.fmt} $</h4>
          </div>
        );
    })}
    

    Here is the full sandbox:

    Edit react-filter-datas