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

ReactJS如何通过POST请求更新API布尔字段is_archived并解决400报错

错误原因排查

1. 按钮点击事件绑定错误

Activity组件中的归档按钮onClick直接执行了archiveCall函数,而非传入点击时触发的函数引用,导致组件渲染时就自动批量发起请求,请求参数异常触发400。
原代码:

onClick={archiveCall(props.call.id)}

2. fetch配置错误

archiveCall方法中添加了mode: 'no-cors'配置,该配置会强制请求为不透明跨域模式,禁止携带自定义请求头、也无法传输JSON格式的请求体,后端无法解析请求参数返回400。

3. 归档后未同步前端状态

请求完成后没有更新App组件中的calls状态,就算归档成功对应通话记录也不会从页面移除。

4. 全量归档逻辑仅修改本地状态

现有handleArchive方法只是清空了本地state的calls数组,没有调用后端接口批量更新is_archived字段,刷新页面后数据会重置。

修复方案

第一步:修改App.jsx,添加单条归档方法并透传props

import React, { Component} from 'react';
import { ActivityFeed } from './components/activity-feed/activity-feed.component.jsx';
import Header from './Header.jsx';

class App extends Component {
  constructor() {
    super();
    this.state = {
      calls: [],
      showMessage: false,
      is_archived: false
    };
  }

  componentDidMount() {
    fetch('https://aircall-job.herokuapp.com/activities')
      .then(response => response.json())
      .then(activities => this.setState({ calls: activities }))
    document.getElementById("reset").disabled = true;
  }

  // 新增单条通话归档方法
  archiveSingleCall = (id) => {
    fetch(`https://aircall-job.herokuapp.com/activities/${id}`, {
      method: "POST",
      headers: {
        'Accept' : 'application/json',
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        is_archived: true
      }),
    }).then(res => res.json())
    .then(() => {
      // 归档成功后过滤掉对应id的通话记录
      this.setState(prev => ({
        calls: prev.calls.filter(call => call.id !== id)
      }))
    })
  }

  handleArchive = event => {
    // 全量归档批量调用接口
    Promise.all(this.state.calls.map(call => 
      fetch(`https://aircall-job.herokuapp.com/activities/${call.id}`, {
        method: "POST",
        headers: {
          'Accept' : 'application/json',
          "Content-Type": "application/json"
        },
        body: JSON.stringify({is_archived: true})
      })
    )).then(() => {
      this.setState({calls: [], showMessage: true });
      document.getElementById("archive").disabled = true;
      document.getElementById("reset").disabled = false;
    })
  };

  handleReset = event => {
    this.componentDidMount();
    this.setState({ showMessage: false });
    document.getElementById("reset").disabled = true;
    document.getElementById("archive").disabled = false;
   };

  render() {
    const { calls, showMessage } = this.state;
    return (
      <div className='App'>
        <Header/>
        {/* 把单条归档方法传给子组件 */}
        <ActivityFeed calls={calls} archiveSingleCall={this.archiveSingleCall} />
        <button type="button" className="archive-btn" id="archive"
        onClick={this.handleArchive}>Archive All Calls</button>
        {showMessage && <p>All calls have been archived</p>}

        <button type="button" className="reset-btn" id="reset"
        onClick={this.handleReset}>Reset Archived Calls</button>
      </div>
    );
  };
}

export default App;

第二步:修改ActivityFeed.component.jsx,透传归档方法

import React from 'react';
import { Activity } from '../activity-detail/activity-detail.component';
import './activity-feed.styles.css';

export const ActivityFeed = props => (
  <div className='activity-feed'>
    {props.calls.map(call => ( 
        <Activity key={call.id} call={call} archiveSingleCall={props.archiveSingleCall}/>
    ))}
  </div>
);

第三步:修改Activity.component.jsx,修正点击事件、移除冗余逻辑

import React from 'react';
import './activity-detail.styles.css';
import missed from '../../resources/images/missed.svg';
import answered from '../../resources/images/answered.svg';
import voicemail from '../../resources/images/voicemail.svg';

function formatDate(date) {
  var localDate = new Date(date);
  return localDate.toDateString().split(' ').slice(1).join(' ');
}

function formatTime(time) {
  var localTime = new Date(time);
  return localTime.toLocaleTimeString().replace(/(.*)\D\d+/, '$1');
}

function callType(type) {
  if (type === "missed") {
   return <img src={missed}  alt="missed" className="call-icon"/>
  }
  else if (type === "answered") {
    return <img src= {answered} alt="answered" className="call-icon"/>
  }
  else 
  return <img src= {voicemail} alt="voicemail" className="call-icon"/>
}

export const Activity = props => (
  <div className='activity-container'>
    <p> Date {formatDate(props.call.created_at)}  </p>
    <p> Time {formatTime(props.call.created_at)}  </p>
    <p> From {props.call.from} </p>
    <p> To {props.call.to} </p>
    <p> Via {props.call.via} </p>
    <p> Call type {callType(props.call.call_type)} </p>
    {/* 修正点击事件为箭头函数,点击时才触发归档 */}
    <button 
      type="button" 
      className="archive-call" 
      id="archive-call"
      onClick={() => props.archiveSingleCall(props.call.id)}
    >Archive call</button>
  </div>
);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 01:09:03