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

React子组件Submenu的useEffect未触发,如何在父组件数据加载后执行?

问题描述

我有一个用于渲染页面的React组件InfoForPatPage,想要实现一个能动态生成页面子菜单的Submenu组件。需要先从父组件获取首个id,再用这个id请求子菜单数据,但Submenu里的useEffect没执行数据请求;如果移除try-catch,会提示id未定义。求解决:如何让Submenu的useEffect在父组件数据全部获取完成后触发。


父组件代码
import Submenu from '../Submenu.js'
import React, {useEffect, useState} from 'react';
import * as myUtilities from '../Utilities';

function InfoForPatPage (props) {
  const [Obsah,setObsah] = useState([]);
  const [image,setimage] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");
  const [id, setId] = useState("");
  
  const loadAsyncData = async () => {
    setIsLoading(true);
    setError(null);
    
    try {
      const resp = await fetch(myUtilities.translateApiUrl("/pages/?type=web.InfoForPatientsPage")).then(r=>r.json());
      const id = await resp.items[0].id;
      const data = await fetch(myUtilities.translateApiUrl("/pages/"+id+"/")).then(r=>r.json());
      const img = await data.banner.meta
      console.log(id)
      setObsah(data);
      setimage(img)
      setId(id)
      setIsLoading(false);
    } catch(e) {
      setError(e);
      setIsLoading(false);
    }
  }

  useEffect(() => {
    loadAsyncData();
  }, []);

  return (
    // ... 其他页面内容
    <Submenu id={ id } />
    // ... 其他页面内容
  )
}
export default InfoForPatPage;

子组件代码
import React, {useEffect, useState} from 'react';
import * as myUtilities from './Utilities';

function Submenu (props) {
  const [Obsah,setObsah] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");
  
  const id = props.id

  const loadAsyncSubmenu = async () => {
    setIsLoading(true);
    setError(null);
    
    try {
      console.log(url)
      const short_url = "/pages/?child_of=" + id +"&fields=_,id"
      const url = myUtilities.translateApiUrl(short_url)
      console.log(url)
      const resp = await fetch(url).then(r=>r.json());
      console.log(resp)
      const childs = []

      for (let index = 0; index < resp.items.length; index++) {
        const page_short_url = "/pages/"+ resp.items[index].id +"/?fields=_,title,ikona"
        const page_url = myUtilities.translateApiUrl(short_url)
        const child = await fetch(url).then(r=>r.json());
        childs.push(child)
      }

      console.log(childs)
     
      setObsah(childs);
      setIsLoading(false);
    } catch(e) {
      setError(e);
      setIsLoading(false);
    }
  }

  useEffect(() => {
    loadAsyncSubmenu();
  }, []);

  return (
    <div class="submenu " id="buttons">
      {Obsah.map ( item => ( 
        <div class="submentu_item "> 
          <img src={item.ikona.meta.download_url} /> 
          <a href="{{ child.get_url }}#buttons" />
        </div>
      ))}
    </div>
  )
}

export default Submenu;

解决方案

1. 让子组件useEffect随id变化触发

父组件的id初始值为空字符串,子组件首次渲染时id无效,此时执行请求会报错。需要给useEffect添加id依赖,且仅在id有效时执行请求:

useEffect(() => {
  // 只有id存在时才发起请求
  if (id) {
    loadAsyncSubmenu();
  }
}, [id]); // 将id加入依赖数组,id更新时重新执行

2. 修复子组件内部代码错误

  • 移除try块开头的console.log(url):此时url还未定义,会直接触发catch导致请求逻辑中断
  • 修正循环内的请求地址:把page_url = myUtilities.translateApiUrl(short_url)改成page_url = myUtilities.translateApiUrl(page_short_url),同时fetch(url)改为fetch(page_url),否则获取的都是子菜单列表而非单个子页面数据
  • React中替换class为className:避免和JS关键字冲突
  • 修正链接模板语法:把{{ child.get_url }}改成React支持的模板字符串,比如${item.get_url},同时要给map的元素添加key属性

修正后的子组件核心代码:

const loadAsyncSubmenu = async () => {
  setIsLoading(true);
  setError(null);
  
  try {
    const short_url = "/pages/?child_of=" + id +"&fields=_,id"
    const url = myUtilities.translateApiUrl(short_url)
    const resp = await fetch(url).then(r=>r.json());
    const childs = []

    for (let index = 0; index < resp.items.length; index++) {
      const page_short_url = "/pages/"+ resp.items[index].id +"/?fields=_,title,ikona"
      const page_url = myUtilities.translateApiUrl(page_short_url)
      const child = await fetch(page_url).then(r=>r.json());
      childs.push(child)
    }

    setObsah(childs);
    setIsLoading(false);
  } catch(e) {
    setError(e);
    setIsLoading(false);
  }
}

useEffect(() => {
  if (id) {
    loadAsyncSubmenu();
  }
}, [id]);

return (
  <div className="submenu" id="buttons">
    {Obsah.map(item => ( 
      <div className="submentu_item" key={item.id}>
        <img src={item.ikona.meta.download_url} alt={item.title} /> 
        <a href={`${item.get_url}#buttons`}>{item.title}</a>
      </div>
    ))}
  </div>
)

3. 父组件可选优化:控制子组件渲染时机

父组件可以在加载完成且id有效时再渲染Submenu,避免子组件接收空id:

return (
  // ... 其他内容
  {!isLoading && id && <Submenu id={id} />}
  // ... 其他内容
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:48:27