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
相关产品推荐
相关产品推荐

