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

React onClick点击任意项始终仅作用于列表最后一个元素问题

问题描述

开发React导航栏组件时,在Navbar组件内绑定onClick事件后,点击导航列表任意元素,事件始终只作用于列表最后一个元素,所有点击操作都只会修改最后一个元素的样式。
涉及的原有代码如下:

import './Navbar.scss';
import ListItem from '../ListItem/ListItem';
import { navLinks } from '../../staticData';
import { useRef } from 'react';

const Navbar = () => {
    const itemRef = useRef();
    const handleClick = () => {
        itemRef.current.style.background = 'red'
    };

  return (
    <div className='navbar'>
        <ul className="navbarList">
            {
                navLinks.map((item, index) => (
                    <ListItem item={item} key={index} onClick={handleClick} itemRef={itemRef}/>
                ))
            }
        </ul>
    </div>
  )
}

export default Navbar

ListItem组件原有代码

import './ListItem.scss';
import { Link } from 'react-router-dom';

const ListItem = ({ item, onClick, itemRef }) => {
  return (
    <Link onClick={onClick} ref={itemRef} className='listItemWrapper' to=''>
      <li className="listItem">{item.name}</li>
    </Link>
  )
}

export default ListItem
问题根因

你在Navbar组件中只创建了单个ref实例,map循环渲染所有ListItem时,所有列表项都绑定了这同一个ref。组件挂载阶段,每个绑定该ref的DOM节点会依次赋值给itemRef.current,循环结束后itemRef.current只会保留最后一个渲染的列表项DOM引用,因此无论点击哪个导航项,事件触发时修改的永远是最后一个元素的样式。

修复方案

方案1:状态驱动样式(官方推荐写法)

React本身是数据驱动视图的框架,不推荐直接操作DOM修改样式,完全可以通过组件状态记录当前激活的导航项,动态匹配样式类实现需求,可维护性更强:

修改后的Navbar组件

import './Navbar.scss';
import ListItem from '../ListItem/ListItem';
import { navLinks } from '../../staticData';
import { useState } from 'react';

const Navbar = () => {
    // 记录当前激活的导航项索引,默认可设置首个导航项为激活态
    const [activeIndex, setActiveIndex] = useState(0);

    const handleClick = (index) => {
        setActiveIndex(index);
    };

  return (
    <div className='navbar'>
        <ul className="navbarList">
            {
                navLinks.map((item, index) => (
                    <ListItem 
                        item={item} 
                        key={index} 
                        onClick={() => handleClick(index)} 
                        isActive={activeIndex === index}
                    />
                ))
            }
        </ul>
    </div>
  )
}

export default Navbar

修改后的ListItem组件

根据父组件传入的激活标识,动态拼接样式类,不需要接收ref:

import './ListItem.scss';
import { Link } from 'react-router-dom';

const ListItem = ({ item, onClick, isActive }) => {
  return (
    <Link 
      onClick={onClick} 
      className={`listItemWrapper ${isActive ? 'active' : ''}`} 
      to=''
    >
      <li className="listItem">{item.name}</li>
    </Link>
  )
}

export default ListItem

补充样式

在ListItem.scss中新增激活态样式即可:

.listItemWrapper.active {
  background: red;
}

方案2:ref数组存储DOM引用(特殊场景使用)

如果业务场景必须直接操作DOM,不要使用单个ref绑定所有列表项,初始化ref数组存储每个导航项的独立DOM引用,点击时传入索引定位到对应DOM操作即可:

修改后的Navbar组件

import './Navbar.scss';
import ListItem from '../ListItem/ListItem';
import { navLinks } from '../../staticData';
import { useRef } from 'react';

const Navbar = () => {
    // 初始化ref数组,长度和导航列表一致
    const itemRefs = useRef([]);
    const handleClick = (index) => {
        // 先重置所有项的样式,避免多个项同时保留红色背景
        itemRefs.current.forEach(ref => {
          if(ref) ref.style.background = '';
        });
        // 仅修改当前点击项的样式
        itemRefs.current[index].style.background = 'red';
    };

  return (
    <div className='navbar'>
        <ul className="navbarList">
            {
                navLinks.map((item, index) => (
                    <ListItem 
                        item={item} 
                        key={index} 
                        onClick={() => handleClick(index)} 
                        // 将每个列表项DOM存入ref数组对应索引位置
                        itemRef={el => itemRefs.current[index] = el}
                    />
                ))
            }
        </ul>
    </div>
  )
}

export default Navbar

ListItem组件保持原有逻辑无需修改即可正常运行。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 13:21:20