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

使用React Context时Badge组件样式更新异常问题

问题:React标签云点击后样式不更新

技术栈

ReactJS + TailwindCSS

功能需求

开发标签云功能:用户点击标签后,选中的标签数组传递到新视图,同时选中标签呈现对应样式。

当前实现与问题

通过父组件渲染Badge子组件列表实现功能:

  • Badge用isSelected状态变量处理样式
  • 借助Context将选中标签值存入选中数组

单独测试各部分逻辑正常:选中时Badge样式正确,值能存入Context数组且可被其他组件访问。但同时更新组件状态与Context时,值虽能加入数组,Badge点击后无法反映样式变化。

尝试将样式判断逻辑包裹在useEffect监听isSelected变化,结果样式完全无法加载。


代码片段

Badge组件

import PropTypes, { bool } from 'prop-types'
import { useContext, useState } from 'react'
import { SkillsContext } from "../context"

function Badge({ className, text, selected = false }) {
  const [isSelected, setIsSelected] = useState(selected)
  const { skills, setSkills } = useContext(SkillsContext)

  if (isSelected) {
    className += ' bg-primary text-black'
  } else {
    className += ' text-white ring-1 ring-inset ring-white'
  }

  const handleSelection = () => {
    setSkills((prev) => [...prev, text])
    setIsSelected((prev) => !prev)
  }

  return (
    <span id="badge" className={`inline-flex items-center rounded-full px-2 md:px-4 py-1 text-sm md:text-base ml-0 font-thin cursor-pointer ${className}`} onClick={handleSelection}>
      {text}
    </span>
  )
}

Badge.propTypes = {
  className: PropTypes.string,
  text: PropTypes.string,
  selected: bool
}

export default Badge

尝试的useEffect修改

useEffect(() => {
 if (isSelected) {
    className += ' bg-primary text-black'
  } else {
    className += ' text-white ring-1 ring-inset ring-white'
  }

}, [isSelected])

父组件HomeSkills

import Button from "../components/Button"
import Badge from "../components/Badge"
import { SkillsContext } from "../context"

import { I18N } from "../i18n"
import { useState } from "react"

function HomeSkills() {
  const { title, skillList, button } = I18N.skills
  const [ skills, setSkills ] = useState([])

  return (
    <SkillsContext.Provider value={{ skills, setSkills }}>
      <div className="wrapper">
        <div className="py-24 px-5 md:px-12 flex flex-col items-center">
            <h2 className="text-white mb-12">{ title }</h2>
            <div className="flex flex-wrap justify-center gap-4 mb-12">
              { skillList.map((skill) => <Badge key={crypto.randomUUID()} text={skill} /> ) }
            </div>
            <Button text={button} />
        </div>
      </div>
    </SkillsContext.Provider>
  )
}

export default HomeSkills

Context.js

import { createContext } from "react";

export const SkillsContext = createContext()

解决方案

核心问题分析

  1. 重复状态管理:Badge组件同时维护本地isSelected状态和Context选中数组,两个状态源未同步,导致样式更新异常;且当前点击逻辑会重复添加同一标签到数组。
  2. className操作不规范:直接修改传入的className props,渲染时会被初始值覆盖;用useEffect修改无效,因为props是不可变的。

修复步骤

  1. 统一状态来源:去掉Badge本地isSelected状态,直接从Context的skills数组推导当前标签是否被选中,确保状态唯一。
  2. 优化点击逻辑:点击时判断标签是否已在数组中,实现选中/取消的切换效果,避免重复添加。
  3. 正确拼接样式:用条件模板字符串拼接className,不修改原始props。

修复后的Badge组件

import PropTypes, { bool } from 'prop-types'
import { useContext } from 'react'
import { SkillsContext } from "../context"

function Badge({ className = '', text, selected = false }) {
  const { skills, setSkills } = useContext(SkillsContext)
  // 从Context数组推导选中状态,支持初始选中值
  const isSelected = skills.includes(text) || selected

  // 条件拼接样式,不修改原始className
  const badgeClass = `${className} inline-flex items-center rounded-full px-2 md:px-4 py-1 text-sm md:text-base ml-0 font-thin cursor-pointer ${
    isSelected ? 'bg-primary text-black' : 'text-white ring-1 ring-inset ring-white'
  }`

  const handleSelection = () => {
    setSkills(prev => {
      if (prev.includes(text)) {
        // 已选中则移除
        return prev.filter(item => item !== text)
      } else {
        // 未选中则添加
        return [...prev, text]
      }
    })
  }

  return (
    <span id="badge" className={badgeClass} onClick={handleSelection}>
      {text}
    </span>
  )
}

Badge.propTypes = {
  className: PropTypes.string,
  text: PropTypes.string,
  selected: PropTypes.bool
}

export default Badge

父组件优化(可选)

避免用crypto.randomUUID()作为key(每次渲染生成新key会导致组件重复挂载),改用标签文本作为唯一key:

{ skillList.map((skill) => <Badge key={skill} text={skill} /> ) }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:48:21