React中array.findIndex报“不是函数”错误及onClick自动触发状态更新问题排查
React状态管理与事件绑定问题排查与修复
我来帮你一步步解决这两个问题,顺便纠正一个React状态管理的核心误区:
问题1:findIndex is not a function 报错
你最开始用state.findIndex报错,原因很直接:你的全局state是一个对象(包含ids数组属性),而findIndex是数组专属方法,对象本身没有这个方法。所以必须先访问到数组state.ids,再调用findIndex——你改成state.ids.findIndex的操作完全正确,这个问题就是对变量类型的小误解导致的~
问题2:点击事件提前触发,所有active被设为true
你现在的onClick={changeActiveField(i.id)}写法,会在组件渲染的时候就立刻执行函数,而不是等用户点击时才触发。因为JSX里的onClick需要接收一个函数引用,你现在直接调用函数,相当于把函数执行的结果(undefined)传给了onClick,自然会提前执行。
修复方法很简单,用箭头函数包裹起来,让它变成一个点击时才会执行的回调:
onClick={() => changeActiveField(i.id)}
隐藏的核心问题:全局state无法触发组件重渲染
你现在用的是全局var state,但React组件的状态更新必须依赖自身的this.state和setState方法——直接修改全局变量不会触发组件重新渲染,所以哪怕你改了active的值,页面也不会同步更新。这才是你点击后看不到预期效果的关键!
完整修复方案
把全局state移到组件的state里,并用setState来规范更新状态:
- 重构组件的state初始化:
constructor(props) { super(props); // 把全局的ids移到组件自身的state中 this.state = { ids: [ { id: "iCBvfW08jlo", active: true }, { id: "qvOcCQXZVg0", active: false }, { id: "YXNC3GKmjgk", active: false }, ] }; // 绑定函数上下文(也可以用箭头函数定义组件方法替代) this.changeActiveField = this.changeActiveField.bind(this); }
- 修改
changeActiveField函数,用setState更新状态(注意不要直接修改原state,要创建新数组保证不可变性):
changeActiveField(targetId) { this.setState(prevState => { // 复制原数组,避免直接修改state const newIds = [...prevState.ids]; // 找到之前active为true的项并设为false const activeIndex = newIds.findIndex(obj => obj.active === true); if (activeIndex !== -1) { newIds[activeIndex] = {...newIds[activeIndex], active: false}; } // 找到点击的项并设为true const targetIndex = newIds.findIndex(obj => obj.id === targetId); if (targetIndex !== -1) { newIds[targetIndex] = {...newIds[targetIndex], active: true}; } return { ids: newIds }; }); }
- 修改渲染部分的代码,全部使用组件自身的
this.state:
- 优化
firstActiveId函数:
function firstActiveId(ids) { const activeItem = ids.find(obj => obj.active); return activeItem ? activeItem.id : ''; }
- 替换全局state为
this.state,并给列表项添加唯一key:
<Embed active autoplay={false} color="white" hd={false} id={firstActiveId(this.state.ids)} iframe={{ allowFullScreen: true, style: { padding: 10 } }} placeholder={`http://img.youtube.com/vi/${firstActiveId(this.state.ids)}/hqdefault.jpg`} source="youtube" /> <div className="thumbs"> {this.state.ids.map((item) => ( <React.Fragment key={item.id}> {/* 必须加key,避免React渲染警告 */} <Image className="carouselitem" rounded fluid onClick={() => this.changeActiveField(item.id)} src={`http://img.youtube.com/vi/${item.id}/hqdefault.jpg`} size="small" /> <h2> {item.id} {item.active ? "true" : "false"} </h2> </React.Fragment> ))} </div>
- 最后移除原来的全局
var state,现在组件完全用自身的state管理状态。
这样修改后,不仅解决了报错和事件提前触发的问题,还能保证状态更新后组件会自动重新渲染,点击缩略图就能正常切换active状态和对应的视频了~
内容的提问来源于stack exchange,提问作者user14181962
相关产品推荐
相关产品推荐

