React点击label触发useState为何两次渲染?表单切换异常
问题:点击label触发两次状态更新,绑定input则功能正常的原因
代码示例
const [searchClick, setSearchClick] = useState("hidden") // Button when clicked the form appears or disappears <label onClick={() => setSearchClick(prevState => prevState ==='hidden' ? 'flex' : 'hidden') } className="btn-xs btn-ghost rounded-full w-fit h-fit swap swap-rotate" > <input type="checkbox" /> <Search className="swap-off relative right-1" width="22px" height="22px" fill='#8DD0BA' /> <svg className="swap-on fill-current" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 512 512"><polygon points="400 145.49 366.51 112 256 222.51 145.49 112 112 145.49 222.51 256 112 366.51 145.49 400 256 289.49 366.51 400 400 366.51 289.49 256 400 145.49"/></svg> </label> <form className={`${searchClick} my-6 justify-center`}> <input type="text" placeholder="Search" className="input input-bordered max-w-lg bg-white text-black" /> <button className="btn w-18 rounded-r-none"><Search /></button> </form>
问题描述
上述代码通过useState控制表单的显示/隐藏,将onClick事件绑定在<label>上时,会触发两次状态更新(两次渲染),导致表单显示/隐藏功能异常;但将事件绑定在内部的<input type="checkbox">上时,功能完全正常。
原因解析
这是HTML原生行为导致的:
<label>元素和其内部的表单控件(这里是<input type="checkbox">)会自动建立关联,点击<label>时,浏览器会自动触发内部input的点击事件(这是label的默认交互行为,用于提升表单可访问性)。- 当你把
onClick绑定在label上时,点击label会先执行你定义的setSearchClick逻辑,随后浏览器的默认行为会触发input的点击事件;而input的点击事件会通过事件冒泡传递到父元素label,再次触发label的onClick逻辑。 - 两次连续调用
setSearchClick会让状态在"hidden"和"flex"之间切换两次,最终回到初始值,看起来功能失效,同时触发两次组件渲染。
解决方案
- 将事件绑定到input上:这是你已经验证有效的方式,点击label时,浏览器默认触发input的点击,只会执行一次状态更新逻辑。
- 阻止label的默认行为:在label的
onClick中调用e.preventDefault(),关闭浏览器自动触发input点击的默认行为,避免重复执行:<label onClick={(e) => { e.preventDefault(); setSearchClick(prevState => prevState ==='hidden' ? 'flex' : 'hidden') }} className="btn-xs btn-ghost rounded-full w-fit h-fit swap swap-rotate" > - 移除不必要的checkbox:如果不需要checkbox的选中状态,直接去掉
<input type="checkbox">,用label作为纯按钮使用,避免默认行为干扰。
内容的提问来源于stack exchange,提问作者Abdullah Manafikhi
相关产品推荐
相关产品推荐

