skip to Main Content

I have a React website and use server side rendering. My web page is displayed in the page source in the browser and also on the web page. Except the API data. This is only displayed in the page source and not on the web page. There I get the error message: Uncaught TypeError: Cannot read properties of undefined (reading ‘map’)

I have here once the server.js, where I fetch the API data and then pass it to the appropriate component. This also works correctly, because if I do console.log(data), then this data is also displayed in the console. But they are not mapped.

Leistungen.jsx

import React, { useState, useEffect } from 'react';
import { LeistungenContainer, LeistungenHeadline, LeistungenText, LeistungenKasten, TextLink, KastenLink, Titel, Headline, Text, Button } from './Leistungen.elements'
import { FaSign } from 'react-icons/fa'
import { TiBusinessCard } from 'react-icons/ti'
import { GiPapers } from 'react-icons/gi'
import { AiTwotoneBoxPlot } from 'react-icons/ai'
import { BsBoxFill } from 'react-icons/bs'

const leistungenIconsLinks = [
    {Leistungsart: "Werbeanlagen & Beschilderung", icon: <FaSign size={50} color="white"/>, link: "/werbeanlagen"},
    {Leistungsart: "Werbeartikel", icon: <TiBusinessCard size={50} color="white"/>, link: "/werbeartikel"},
    {Leistungsart: "Folierung & Drucken", icon: <GiPapers size={50} color="white"/>, link: "/folierung"},
    {Leistungsart: "Verschiedene Banner", icon: <AiTwotoneBoxPlot size={50} color="white"/>, link: "/banner"},
    {Leistungsart: "Leuchtkasten & Leuchtreklame", icon: <BsBoxFill size={50} color="white"/>, link: "/leuchtkasten"}
  ];

const Leistungen = ({data}) => {

  return (
    <>
      <LeistungenContainer>
        <LeistungenHeadline>
          <Headline>Unsere<br /><span>Leistungen</span></Headline>
          <LeistungenText>Test</LeistungenText>
        </LeistungenHeadline>
        {data.map((leistung, i) => {
          const iconLink = leistungenIconsLinks.find(item => item.Leistungsart === leistung.attributes.Leistungsname);
          return (
            <LeistungenKasten>
              <KastenLink to={iconLink.link}>
                {iconLink.icon}
                <Titel>{leistung.attributes.Leistungsname}</Titel>
                <Text>{leistung.attributes.Leistungsbeschreibung}</Text>
                <Button to={iconLink.link}>Erfahre mehr</Button>
              </KastenLink>
            </LeistungenKasten>
          );
        })}
      </LeistungenContainer>
    </>
  )
}

export default Leistungen

server.js

import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import express from 'express'
import { Helmet } from 'react-helmet';
import fetch from 'node-fetch';
import https from 'https'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

const isTest = process.env.VITEST

export async function createServer(
  root = process.cwd(),
  isProd = process.env.NODE_ENV === 'production',
  hmrPort,
) {
  const resolve = (p) => path.resolve(__dirname, p)

  const indexProd = isProd
    ? fs.readFileSync(resolve('dist/client/index.html'), 'utf-8')
    : ''

  const app = express()

  /**
   * @type {import('vite').ViteDevServer}
   */
  let vite
  if (!isProd) {
    vite = await (
      await import('vite')
    ).createServer({
      root,
      logLevel: isTest ? 'error' : 'info',
      server: {
        middlewareMode: true,
        watch: {
          // During tests we edit the files too fast and sometimes chokidar
          // misses change events, so enforce polling for consistency
          usePolling: true,
          interval: 100,
        },
        hmr: {
          port: hmrPort,
        },
      },
      appType: 'custom',
    })
    // use vite's connect instance as middleware
    app.use(vite.middlewares)
  } else {
    app.use((await import('compression')).default())
    app.use(
      (await import('serve-static')).default(resolve('dist/client'), {
        index: false,
      }),
    )
  }

  const agent = new https.Agent({
    rejectUnauthorized: false
  });
  
  
  app.use('*', async (req, res) => {
    try {
      const url = req.originalUrl

      let template, render
      if (!isProd) {
        // always read fresh template in dev
        template = fs.readFileSync(resolve('index.html'), 'utf-8')
        template = await vite.transformIndexHtml(url, template)
        render = (await vite.ssrLoadModule('/src/entry-server.jsx')).render
      } else {
        template = indexProd
        // @ts-ignore
        render = (await import('./dist/server/entry-server.js')).render
      }


      const context = {}
      const response = await fetch('https://hamburger-werbeagentur.de/admin/api/startseite-leistungens', { agent });
      const data = (await response.json()).data;
      const appHtml = render(url, context, data);
      const helmet = Helmet.renderStatic();

      if (context.url) {
        // Somewhere a `<Redirect>` was rendered
        return res.redirect(301, context.url)
      }

      const html = template
        .replace(`<!--app-html-->`, appHtml)
        .replace(`<!--app-head-->`, `${helmet.title.toString()}${helmet.meta.toString()}`)


      res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
    } catch (e) {
      !isProd && vite.ssrFixStacktrace(e)
      console.log(e.stack)
      res.status(500).end(e.stack)
    }
  })


  return { app, vite }
}

if (!isTest) {
  createServer().then(({ app }) =>
    app.listen(5175, () => {
      console.log('http://localhost:5175')
    }),
  )
}

2

Answers


  1. What exactly is in the data argument passed to the Leistungen component? The case might be that as the state changes, at some point undefined is passed into it. The easiest solution would be to use optional chaining (question mark operator) like this:

    data?.map(leistung, i) => { ...
    

    This ensures that the map is called only when data is not undefined/null. However, a better solution would be to render something like a Loading component when data is not available.

    data ? data.map((leistung, i) => { ...your code here }) : <Loading />
    
    Login or Signup to reply.
  2. Use data && data operator or optional chaining like this data?.map(()),
    once requesting data from server you must use && operator to hold the flow untill the response completes

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search