React中能否在map内调用自定义Hook?如何规避Hook规则
解决方案
方案1:拆分子组件(最直接符合Hook规则的方式)
把每条操作记录的渲染逻辑抽成独立子组件,在子组件的顶层调用自定义Hook,完全符合React的Hook规则:
// 子组件:单独处理单条操作记录 const SuspendHistoryItem = ({ item }) => { // 在组件顶层调用Hook,符合规则 const suspenderName = useGetUserName(item.susBy); const reinstaterName = item.reinBy ? useGetUserName(item.reinBy) : null; return ( <ol> <li>Suspended on: {new Date(item.susOn).toLocaleDateString()}</li> <li>Suspended By: {suspenderName}</li> {reinstaterName && ( <> <li>Reinstated on: {new Date(item.reinOn).toLocaleDateString()}</li> <li>Reinstated by: {reinstaterName}</li> </> )} <br /> </ol> ); }; // 父组件里的用法 useEffect(() => { if (suspendHistory?.length) { setNamedHistory( suspendHistory.map((item, idx) => ( <SuspendHistoryItem key={idx} item={item} /> )) ); } }, [suspendHistory]);
方案2:改造自定义Hook为批量获取(更高效)
如果不想拆分子组件,可以把useGetUserName改成支持批量获取用户名称的Hook,在父组件顶层调用后,再在map里匹配对应的名称:
// 改造后的批量Hook const useGetUserNames = (userIds) => { const [userNames, setUserNames] = useState({}); useEffect(() => { // 去重避免重复请求 const uniqueIds = [...new Set(userIds.filter(Boolean))]; if (!uniqueIds.length) return; // 调用后端批量接口(如果后端支持),或者并行请求单个接口 Promise.all(uniqueIds.map(id => fetchUserNameById(id))) .then(results => { const nameMap = {}; uniqueIds.forEach((id, index) => { nameMap[id] = results[index]; }); setUserNames(nameMap); }); }, [userIds]); return userNames; }; // 父组件里的用法 const ParentComponent = () => { // 收集所有需要的用户ID const allUserIds = suspendHistory?.flatMap(item => [item.susBy, item.reinBy]).filter(Boolean) || []; // 在组件顶层调用批量Hook const userNameMap = useGetUserNames(allUserIds); // 直接渲染,不需要useEffect存组件 return ( <div> {suspendHistory?.map((item, idx) => ( <ol key={idx}> <li>Suspended on: {new Date(item.susOn).toLocaleDateString()}</li> <li>Suspended By: {userNameMap[item.susBy]}</li> {item.reinBy && ( <> <li>Reinstated on: {new Date(item.reinOn).toLocaleDateString()}</li> <li>Reinstated by: {userNameMap[item.reinBy]}</li> </> )} <br /> </ol> ))} </div> ); };
方案3:后端返回关联数据(最省心的长期方案)
如果后端可以调整,建议在获取suspendHistory接口的时候,直接把susBy和reinBy对应的用户名称一起返回(比如嵌套对象:susBy: { id: 'xxx', name: '张三' }),这样前端不需要额外发起请求,也不用处理Hook的问题,代码会更简洁。
内容的提问来源于stack exchange,提问作者Jowz
相关产品推荐
相关产品推荐

