You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件按需渲染问题:点击按钮触发渲染且避免重复重渲染

问题排查与修复方案

先说说你代码里导致组件不渲染的几个核心问题:

  1. 无限循环请求拖垮组件:FilmTableRows里的useEffect没加依赖数组,每次组件渲染都会触发请求,浏览器一直在重复发请求,状态更新被干扰,自然没法正常渲染内容。
  2. dataFormat取值错误:App里dataFormat初始是空字符串,而且你在switch里用了dataFormat.value——如果SelectDataFormat传的是纯字符串(比如选完直接传"json"),那dataFormat.value就是undefined,直接走到getString分支。但getString返回的是字符串,你用filmList.map遍历字符串的话,每个字符没有title这些属性,渲染出来全是空。
  3. 点击重新渲染的逻辑缺失:现在靠open状态控制组件挂载,第一次点击open变成true组件挂载,但再次点击时open已经是true,组件不会重新触发数据请求,自然看不到更新。
  4. 错误处理完全缺失:所有axios请求的catch都是空的,就算请求失败你也不知道,没法排查问题。
  5. Film构造函数未定义:你代码里用了new Film()但没写这个类的定义,这里会直接报错,导致filmList根本没法正确设置。

接下来是对应的修复步骤,直接改代码就能解决:

1. 给useEffect加依赖数组,停止无限请求

把FilmTableRows里的useEffect改成这样,只有当dataFormat变化时才发请求:

useEffect(() => {
  if (!dataFormat) return; // 避免初始值为空时瞎请求
  switch (dataFormat) {
    case "json":
      getJson();
      break;
    case "xml":
      getXML();
      break;
    default:
      getString();
  }
}, [dataFormat]); // 依赖dataFormat,只在格式变化时执行

2. 修正dataFormat的取值逻辑

如果SelectDataFormat传递的是字符串(比如"json""xml"),直接用dataFormat就行,别加.value。要是传的是对象(比如{value: "json"}),那App里的dataFormat初始值要设成{value: "string"},避免undefined。

3. 实现点击按钮重新渲染的逻辑

在App里加个refreshKey状态,每次点击按钮更新这个key,强制FilmTableRows重新挂载,从而重新请求数据:

// App.js里加状态
const [refreshKey, setRefreshKey] = useState(0);

// 修改openData函数
const openData = () => {
  setOpen(true);
  setRefreshKey(prev => prev + 1); // 每次点击更新key,组件重新挂载
};

// 渲染FilmTableRows时加key属性
<tbody>{open && <FilmTableRows key={refreshKey} closeData={closeData} dataFormat={dataFormat} />}</tbody>

4. 补上错误处理,方便排查问题

每个axios的catch块里加日志,比如:

.catch((err) => {
  console.error('JSON请求失败:', err);
  setFilmList([]); // 请求失败时清空列表,避免旧数据残留
});

5. 定义Film构造函数或者直接用对象

如果没写Film类,补上:

class Film {
  constructor(id, title, year, director, stars, review) {
    this.id = id;
    this.title = title._text || title; // 记得处理xml解析出来的_text属性
    this.year = year._text || year;
    this.director = director._text || director;
    this.stars = stars._text || stars;
    this.review = review._text || review;
  }
}

或者直接用对象字面量代替new Film(),省得写类:

const film = {
  id: f.id._text,
  title: f.title._text || f.title,
  year: f.year._text || f.year,
  director: f.director._text || f.director,
  stars: f.stars._text || f.stars,
  review: f.review._text || f.review
};

6. 修正getString的解析逻辑

getString返回的是字符串,得解析成数组才能用map。假设后端返回的是JSON字符串,改成这样:

const getString = () => {
  let config = {
    headers: {
      "data-type": "string",
      "Content-type": "application/html",
    },
  };
  axios
    .get(baseURL, config)
    .then((res) => {
      try {
        const parsedData = JSON.parse(res.data);
        setFilmList(parsedData);
      } catch (e) {
        console.error('字符串解析失败:', e);
        setFilmList([]);
      }
    })
    .catch((err) => {
      console.error('字符串请求失败:', err);
      setFilmList([]);
    });
};

修改后的完整代码

App.js

import React, { useState } from 'react';
import SelectDataFormat from './SelectDataFormat';
import FilmTableRows from './FilmTableRows';

function App() {
  const [open, setOpen] = useState(false);
  const [dataFormat, setDataFormat] = useState("string"); // 设置默认格式
  const [refreshKey, setRefreshKey] = useState(0);

  const openData = () => {
    setOpen(true);
    setRefreshKey(prev => prev + 1); // 强制组件重新挂载
  };

  const closeData = () =>{
    setOpen(false);
  }

  const changeDataFormat = (selectedOption) => {
    console.log(selectedOption);
    setDataFormat(selectedOption);
  };

  return (
    <main className="App">
      <h1>Film Management</h1>
      <SelectDataFormat changeDataFormat={changeDataFormat} />
      <button onClick={openData}>Show Films</button>
      <table border="1">
        <thead>
          <tr>
            <th>Title</th>
            <th>Year</th>
            <th>Director</th>
            <th>Stars</th>
            <th>Review</th>
          </tr>
        </thead>
        <tbody>{open && <FilmTableRows key={refreshKey} closeData={closeData} dataFormat={dataFormat} />}</tbody>
      </table>
    </main>
  );
}

export default App;

FilmTableRows.js

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import convert from 'xml-js';

class Film {
  constructor(id, title, year, director, stars, review) {
    this.id = id;
    this.title = title._text || title;
    this.year = year._text || year;
    this.director = director._text || director;
    this.stars = stars._text || stars;
    this.review = review._text || review;
  }
}

function FilmTableRows(props) {
  const dataFormat = props.dataFormat;
  const [filmList, setFilmList] = useState([]);
  const baseURL = "http://localhost:8080/FilmRestful/filmapi";

  const getJson = () => {
    let config = {
      headers: {
        "data-type": "json",
        "Content-type": "application/json",
      },
    };

    axios
      .get(baseURL, config)
      .then((res) => {
        setFilmList(res.data);
      })
      .catch((err) => {
        console.error('JSON请求失败:', err);
        setFilmList([]);
      });
  };

  const getXML = () => {
    let config = {
      headers: {
        "data-type": "xml",
        "Content-type": "application/xml",
      },
    };

    axios
      .get(baseURL, config)
      .then((res) => {
        const resData = JSON.parse(convert.xml2json(res.data, { compact: true, spaces: 2 }));
        const films = Array.isArray(resData.films.film) ? resData.films.film : [resData.films.film];
        const newList = films.map(f => new Film(f.id._text, f.title, f.year, f.director, f.stars, f.review));
        setFilmList(newList);
      })
      .catch((err) => {
        console.error('XML请求失败:', err);
        setFilmList([]);
      });
  };

  const getString = () => {
    let config = {
      headers: {
        "data-type": "string",
        "Content-type": "application/html",
      },
    };
    axios
      .get(baseURL, config)
      .then((res) => {
        try {
          const parsedData = JSON.parse(res.data);
          setFilmList(parsedData);
        } catch (e) {
          console.error('字符串解析失败:', e);
          setFilmList([]);
        }
      })
      .catch((err) => {
        console.error('字符串请求失败:', err);
        setFilmList([]);
      });
  };

  useEffect(() => {
    if (!dataFormat) return;
    switch (dataFormat) {
      case "json":
        getJson();
        break;
      case "xml":
        getXML();
        break;
      default:
        getString();
    }
  }, [dataFormat]);

  return (
    <>
      {filmList.map((el) => (
        <tr key={el.id}>
          <td>{el.title}</td>
          <td>{el.year}</td>
          <td>{el.director}</td>
          <td>{el.stars}</td>
          <td>{el.review}</td>
        </tr>
      ))}
    </>
  );
}

export default FilmTableRows;

内容的提问来源于stack exchange,提问作者DKWebservices

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 17:25:26