React组件中map()方法内调用函数引发控制台无限循环打印的问题与实现需求
嘿,我一眼就看出问题所在啦!你遇到的无限打印和循环,根源在于**showImage函数里的setState调用时机不对**。下面给你拆解原因和修复方案:
问题根源
当React组件的render方法执行时,你在map里调用了showImage(task.id),而showImage内部又调用了this.setState({ status: response.data })——setState会触发组件重新渲染,重新渲染又会再次调用showImage,再次setState,这样就陷入了无限循环,控制台自然会无限打印状态值。
另外还有个小问题:你的strikeThroughFn函数里的innerHTML语法错误,应该用模板字符串拼接,不过先解决核心的循环问题。
修复方案
核心思路是:不要在渲染阶段(render或render调用的函数里)触发setState,而是把每个任务的状态提前存在state中,渲染时直接取用。
步骤1:调整State结构
把单个status改成存储所有任务状态的对象,这样每个任务的状态可以独立保存:
constructor(props) { super(props) this.state = { task_one:[], taskStatuses: {} // key: taskId, value: 对应任务的status } this.deleteTask = this.deleteTask.bind(this) }
步骤2:在组件挂载后批量获取任务状态
在componentDidMount里,先获取所有任务,再逐个获取每个任务的状态并存入taskStatuses:
componentDidMount() { TaskOneServices.getTasks().then((response) => { const tasks = response.data; this.setState({ task_one: tasks }, () => { // 组件更新后再请求每个任务的状态,避免提前操作未更新的state tasks.forEach(task => { TaskOneServices.getStatus(task.id).then((res) => { this.setState(prevState => ({ taskStatuses: { ...prevState.taskStatuses, [task.id]: res.data } })); }); }); }); }); }
步骤3:修改showImage函数,直接从State取状态
去掉函数里的异步请求,直接用taskStatuses里存储的状态,这样渲染时不会触发setState:
showImage (taskId) { // 给个默认值,避免初始状态未加载时出错 const status = this.state.taskStatuses[taskId] || "CREATED"; if(status === "CREATED") { return ( <img className="img-lm" src={circle} alt="bullets" onClick={(e) => {this.taskCompleted(e, taskId)}}/> ) } else { return ( <img className="img-lm" src={tick} alt="checked" onClick={(e) => {this.taskCompleted(e, taskId)}}/> ) } }
步骤4:同步更新State中的状态(可选但推荐)
原来的taskCompleted里直接修改DOM的方式不符合React的理念,建议在updateTask成功后更新taskStatuses,让React自动渲染正确的图片:
updateTask(id) { TaskOneServices.updateTask(id).then((response) => { console.log(response); // 假设response.data返回更新后的status,同步到state this.setState(prevState => ({ taskStatuses: { ...prevState.taskStatuses, [id]: response.data } })); }); }
这样你可以删掉taskCompleted里手动修改src和alt的代码,让React根据taskStatuses自动渲染图片。
额外修复:strikeThroughFn语法错误
原来的字符串拼接语法不对,改成模板字符串:
strikeThroughFn(taskid) { console.log(taskid); var fetchTask = document.getElementById('strikeTask'); fetchTask.innerHTML = `<strike>${taskid}</strike>`; }
这样修改后,组件只会在初始化和状态真正变化时渲染,不会再出现无限循环啦!
内容的提问来源于stack exchange,提问作者Aneesh Edavalath S

