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

Next.js页面重载与状态变更后组件渲染异常问题

Next.js页面重载时组件异常嵌套渲染问题

我在开发Next.js项目时遇到了一个棘手的问题:每次重载页面,都会出现组件异常嵌套渲染的情况——RoomCards组件近乎以自身子组件的形式重复渲染,演示效果为页面重载后,原本的RoomCards容器内部又多出一套完整的RoomCards结构。

相关代码

父组件渲染逻辑

<Col md={9} className={styles.Main}>
  {roomsError && <p>{t('common:DATA_LOADING_ERROR_TEXT')}</p>}
  {roomsData.length > 0 && <RoomCards user={user} roomsData={roomsData} />}
  {roomsData.length === 0 && !loading && <RoomCardEmpty/>}
  {loading && <p>{t('ROOMS_LOADING_ROOMS')}</p>}
</Col>

状态定义

const [roomsData, setRoomsData] = useSessionStorage('dorms', []);
const [hasMore, setHasMore] = useState(true);
const [loading, setLoading] = useState(false);
const [roomsError, setRoomsError] = useState(false);

样式代码

.Main {
  overflow-x: hidden;
  padding-bottom: 20px;
}

RoomCards组件代码

export default function RoomCards({
  roomsData = [],
  user = null,
  uploadsPage = false,
  className = '',
  style,
  ...restProps
}) {
  const {t} = useTranslation('rooms');
  const rowClassName = uploadsPage ? 'row-cols-1 row-cols-md-2' : '';

  const colMd = uploadsPage ? null : 10;

  if (roomsData.length === 0) {
    return (
      <>
        {uploadsPage && <p>{t('ROOMS_CARDS_NO_UPLOADED_ROOMS_TEXT')}</p>}
        {!uploadsPage && <p>{t('ROOMS_CARDS_NO_ROOMS_TEXT')}</p>}
      </>
    );
  } else {
    return (
      <Row className={`justify-content-center g-5 ${rowClassName}`} style={uploadsPage ? {paddingBottom: '80px'} : {}}>
        {roomsData.map((roomData, index) => (
          <Col
            key={index}
            md={colMd}
            className={className}
            style={style}
            {...restProps}
          >
            {roomData.external ?
                <ExternalRoomCard
                  user={user}
                  roomData={roomData}
                  uploadsPage={uploadsPage}
                /> :
                <RoomCard
                  user={user}
                  roomData={roomData}
                  uploadsPage={uploadsPage}
                />}
          </Col>
        ))}
      </Row>
    );
  }
}

渲染后HTML对比

  • 重载前:HTML结构正常,RoomCards组件仅渲染一次,内部是对应的RoomCard/ExternalRoomCard子组件
  • 重载后:RoomCards组件内部嵌套了另一层完整的RoomCards结构,出现重复渲染

补充异常说明

再次测试后异常仍存在,相关截图显示:

  1. 组件层级出现多层嵌套,RoomCards被重复包裹
  2. DOM结构中出现重复的RoomCards容器,导致布局混乱

问题定位

  1. key值使用不当:遍历roomsData时用index作为key,页面重载后sessionStorage数据变化时,React无法正确识别组件身份,引发复用错误和重复渲染。
  2. SSR与sessionStorage冲突:Next.js服务端渲染时无法读取浏览器sessionStorage,useSessionStorage默认返回空数组;客户端hydrate时读取存储数据,状态同步不及时导致二次渲染异常。
  3. props传递冗余:RoomCards将...restProps传递给内部Col组件,若父组件传递的属性包含触发渲染的字段,可能引发意外渲染。

解决方案

  1. 替换唯一key值:把遍历的key={index}改为roomData的唯一标识(如roomData.id),确保React能准确区分子组件:
<Col
  key={roomData.id}
  md={colMd}
  className={className}
  style={style}
>
  1. 兼容SSR与sessionStorage:用useState+useEffect替代useSessionStorage,避免服务端与客户端状态不一致:
const [roomsData, setRoomsData] = useState([]);

// 客户端读取sessionStorage
useEffect(() => {
  const storedData = sessionStorage.getItem('dorms');
  if (storedData) {
    setRoomsData(JSON.parse(storedData));
  }
}, []);

// 同步状态到sessionStorage
useEffect(() => {
  sessionStorage.setItem('dorms', JSON.stringify(roomsData));
}, [roomsData]);
  1. 精简props传递:移除不必要的...restProps,只传递Col组件需要的属性,避免意外渲染触发。

  2. 优化条件渲染逻辑:用嵌套三元表达式替代独立&&判断,避免多条件同时满足导致的重复渲染:

<Col md={9} className={styles.Main}>
  {roomsError && <p>{t('common:DATA_LOADING_ERROR_TEXT')}</p>}
  {loading ? (
    <p>{t('ROOMS_LOADING_ROOMS')}</p>
  ) : roomsData.length > 0 ? (
    <RoomCards user={user} roomsData={roomsData} />
  ) : (
    <RoomCardEmpty/>
  )}
</Col>

内容的提问来源于stack exchange,提问作者rowan-vr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 19:40:34