最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

React Server Side Rendering API 数据映射不起作用

网站源码admin41浏览0评论

React Server Side Rendering API 数据映射不起作用

React Server Side Rendering API 数据映射不起作用

我有一个 React 网站并使用服务器端渲染。我的网页显示在浏览器的页面源中,也显示在网页上。 API 数据除外。这仅显示在页面源中,而不显示在网页上。我收到错误消息:Uncaught TypeError: Cannot read properties of undefined (reading 'map')

我这里曾经有 server.js,我在其中获取 API 数据,然后将其传递给适当的组件。这也能正常工作,因为如果我执行 console.log(data),那么该数据也会显示在控制台中。但它们没有映射。

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('', { 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')
    }),
  )
}
回答如下:

传递给 Leistungen 组件的

data
参数到底是什么?情况可能是,随着状态的变化,在某个时候
undefined
被传递给它。最简单的解决方案是像这样使用可选链接(问号运算符):

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

这确保了

map
仅在
data
不是未定义/空时被调用。但是,更好的解决方案是在数据不可用时呈现类似
Loading
组件的内容。

data ? data.map((leistung, i) => { ...your code here }) : <Loading />
发布评论

评论列表(0)

  1. 暂无评论