Search code examples
javascriptreactjsarraysmapping

TypeError: t.map is not a function


The problem I'm dealing with is TypeError: t.map is not a function I'm trying to map my array of jsons to the React components to render them with props.

function App() {
    const charts = renderUserProjects();
    return(
    <div className="app-container">
        <h1 className="app-title">Welcome back! Here are your trackings: </h1>
        <div className="trackings-container">
            <h2 className="trackings-title">👁‍ Watchlist</h2>
            {
                charts.map(chart => (
                    <Collection {...chart} key={chart.symbol} />
                ))
            }
        </div>
    </div>
);

}

I'm mapping the charts array above and I'm creating it below

const renderUserProjects = async() => {
    const userProjects = await fetchUserProjects();
    const charts = []
    for(let x = 0; x < userProjects.length; x++) {
        try {
            const proxyURL = 'https://ixoth-tracker.herokuapp.com/';
            const targetURL = 'https://api-mainnet.magiceden.dev/v2/collections/' + userProjects[x] + '/stats';
            const response = await axios.get(proxyURL + targetURL);
            const data = response.data
            const floorPrice = data["floorPrice"];
            const listedCount = data["listedCount"];
            const volumeAll = data["volumeAll"];
            let chartJSON = {
                symbol: userProjects[x],
                floorPrice: floorPrice,
                listedCount: listedCount,
                volumeAll: volumeAll
            }
            charts.push(JSON.stringify(chartJSON))
        } catch(error) {
            console.error(error);
        }
    }
    console.log(charts)
    return(charts);
}

I looked up on google and most of the errors caused because the element that is being mapped was not an array (usually an object). But in my case I don't think the problem is that, I tried to console.log(Array.isArray(charts)) and it printed out true Can someone help me with this problem?

function Collection(props) {
return(
    <div className="chart-wrapper">
        <p className="project-name">{props.symbol}</p>
        <p className="floor-price">Floor Price: {props.floorPrice}</p>
        <p className="listed-count">Listed Count: {props.listedCount}</p>
        <p className="volume-all">Volume All: {props.volumeAll}</p>
    </div>
)

}


Solution

  • The problem is that renderUserProjects is async function. It returns Promise which doesn't have map method. At the moment of rendering, it hasn't been even resolved. Moreover, each component render you start data fetching.

    In this case, we have to wait until it's resolved.

    function App() {
        // We put [] as default value. Or we can add even a `loading` state in order to show spinner during the data fetching.
        const [charts, setCharts] = useState([]);
      
        useEffect(() => {
            // On mount
            renderUserProjects().then(setChart);
        }, []);
        
        return(
           <div className="app-container">
    

    or as the second option, we can transform renderUserProjects to custom hook useUserProjectCharts and do the same here:

    const useRenderUserProjects = () => {
      const [charts, setCharts] = useState([]);
      const [isLoading, setIsLoading] = useState();
      const [error, setError] = useState();
    
      async function getCharts() {
        setIsLoading(true);
        const userProjects = await fetchUserProjects();
        const charts = [];
    
        for (let x = 0; x < userProjects.length; x++) {
          const proxyURL = 'https://ixoth-tracker.herokuapp.com/';
          const targetURL = 'https://api-mainnet.magiceden.dev/v2/collections/' + userProjects[x] + '/stats';
          const response = await axios.get(proxyURL + targetURL);
          const data = response.data;
          const floorPrice = data['floorPrice'];
          const listedCount = data['listedCount'];
          const volumeAll = data['volumeAll'];
          let chartJSON = {
            symbol: userProjects[x],
            floorPrice: floorPrice,
            listedCount: listedCount,
            volumeAll: volumeAll,
          };
    
          charts.push(JSON.stringify(chartJSON));
        }
      }
    
      useEffect(() => {
        getCharts()
          .then(setCharts)
          .catch(setError)
          .finally(() => setIsLoading(false));
      }, []);
    
      return { charts, isLoading, error };
    };
    

    and use it useRenderUserProjects as in your App component:

    const charts = useRenderUserProjects();