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

React styled components中父组件.map内如何访问子组件state

问题原因
  • visible是Navbar.ListItem组件内部通过useState声明的局部状态,作用域仅在NavbarListItem函数组件内部,外层Navbar组件的map遍历逻辑不在该作用域下,直接访问会触发变量未定义错误。
  • 你已经在ListItem中通过Ctx.Provider向下传递状态,但没有在Provider覆盖的子组件中通过useContext消费对应值,传递的状态没有被实际使用。
解决方案

根据你的组件拆分逻辑,二选一即可:

方案1:保留子组件自治状态,通过Context消费值

不需要改动你原来的状态定义位置,把map中渲染的单个菜单项内容抽为独立组件,在组件内消费Context拿到visible值即可,符合单向数据流原则:

import { createContext, useContext, useState } from 'react';

// 提前定义Context并导出,供子组件消费
const Ctx = createContext<{
  visible: boolean;
  setVisible: (v: boolean | ((prev: boolean) => boolean)) => void;
}>({
  visible: false,
  setVisible: () => {},
});

// 抽离单个菜单项组件,该组件渲染在Ctx.Provider的子树内,可以直接消费Context
function NavMenuItem({ item }: { item: (typeof NavData)[number] }) {
  const { visible } = useContext(Ctx);
  return (
    <Navbar.Link>
      <Navbar.Image src={visible ? item.openIcon : item.closedIcon} />
      <p>{item.name}</p>
    </Navbar.Link>
  );
}

export default function Navbar({ children, ...restProps }: IProps) {
  return (
    <Container {...restProps}>
      <Inner>
        <Navbar.List>
          {NavData.map((item, i) => (
            <Navbar.ListItem key={i}>
              <NavMenuItem item={item} />
            </Navbar.ListItem>
          ))}
        </Navbar.List>
      </Inner>
    </Container>
  );
}

Navbar.List = function NavbarList({ children, ...restProps }: IProps) {
  return <List {...restProps}>{children}</List>;
};

Navbar.ListItem = function NavbarListItem({ children, ...restProps }: IProps) {
  const [visible, setVisible] = useState(false);
  // 原有Provider逻辑保留
  return (
    <Ctx.Provider value={{ visible, setVisible }}>
      <ListItem onClick={() => setVisible((curr) => !curr)} {...restProps}>
        {children}
      </ListItem>
    </Ctx.Provider>
  );
};

Navbar.Link = function NavbarLink({ children, ...restProps }: IProps) {
  return <Link {...restProps}>{children}</Link>;
};

Navbar.Image = function NavbarImage({ children, ...restProps }: IProps) {
  return <Image {...restProps} />;
};

方案2:状态上移到父组件统一管理

如果不想引入Context,可以把visible状态从ListItem上移到Navbar组件统一维护,父组件直接掌握所有菜单项的展开状态,不需要跨组件访问子组件内部值:

import { useState } from 'react';

export default function Navbar({ children, ...restProps }: IProps) {
  // 用数组维护每个菜单项的展开状态,索引和NavData一一对应
  const [visibleList, setVisibleList] = useState<boolean[]>(() => NavData.map(() => false));

  const toggleVisible = (index: number) => {
    setVisibleList(prev => prev.map((val, i) => i === index ? !val : val));
  };

  return (
    <Container {...restProps}>
      <Inner>
        <Navbar.List>
          {NavData.map((item, i) => (
            <Navbar.ListItem key={i} onClick={() => toggleVisible(i)}>
              <Navbar.Link>
                {/* 直接从父组件维护的状态中取值 */}
                <Navbar.Image src={visibleList[i] ? item.openIcon : item.closedIcon} />
                <p>{item.name}</p>
              </Navbar.Link>
            </Navbar.ListItem>
          ))}
        </Navbar.List>
      </Inner>
    </Container>
  );
}

Navbar.List = function NavbarList({ children, ...restProps }: IProps) {
  return <List {...restProps}>{children}</List>;
};

// ListItem不再维护内部状态,直接透传props即可
Navbar.ListItem = function NavbarListItem({ children, ...restProps }: IProps) {
  return <ListItem {...restProps}>{children}</ListItem>;
};

Navbar.Link = function NavbarLink({ children, ...restProps }: IProps) {
  return <Link {...restProps}>{children}</Link>;
};

Navbar.Image = function NavbarImage({ children, ...restProps }: IProps) {
  return <Image {...restProps} />;
};

如果你需要实现手风琴效果(同一时间仅展开一个菜单项),方案2里不需要用数组存状态,直接用一个number类型的state存当前激活项的索引即可,逻辑更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 00:51:27