React结合Fetch API请求成功后无法通过setState存储响应问题
提问内容
我知晓该类问题在技术社区已有大量相关提问,但我查阅了5小时Stack Overflow的相关问题后仍未找到解决方案,因此发帖求助,希望能有人帮我排查代码中的错误。
我在React应用中发起了一个Fetch请求,目前已经可以成功接收到接口响应,但无法将响应数据存储到组件state中。我个人检查代码逻辑未发现明显错误,尝试执行存储操作时没有任何效果,浏览器控制台以及运行React应用的终端均无报错信息。目前相关代码如下(部分内容已做隐私脱敏修改):
loginSubmission = () => { fetch('https://genericsite.com/auth', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({"username": this.state.username, "password": this.state.password}) }) .then(res => res.json()) .then(res => {this.setState({response: res}, () => this.sendResponse())}) .catch(error => { console.log(error); }); } sendResponse(){ console.log(this.state.response) let data = {response: this.state.response}; this.props.receiveResponse(data); }
如果我将then回调中的逻辑改为如下所示的直接打印响应,就可以正常在控制台输出响应内容。我之前查阅同类问题时了解到,console.log存在强制等待请求完成后再打印结果的特性,该测试代码如下:
loginSubmission = () => { fetch('https://genericsite.com/auth', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({"username": this.state.username, "password": this.state.password}) }) .then(res => res.json()) .then(res => {console.log('res.response')}) .catch(error => { console.log(error); }); }
上述测试代码返回的响应对象如下:
{token: 'bigLongJumbledToken', idtoken: '', exp: 1655106045, username: 'myusername'} exp: 1655106045 idtoken: "" token: "bigLongJumbledToken" username: "myusername" [[Prototype]]: Object
我当前组件的state初始化代码如下:
this.state = { username: '', password: '', response: {} } this.userOnChange = this.userOnChange.bind(this); this.passOnChange = this.passOnChange.bind(this); this.loginSubmission = this.loginSubmission.bind(this); }
提前感谢大家提供的任何帮助。
问题解答
按下面的顺序排查,很快能定位问题:
- 第一步先确认setState本身是不是真的没执行
把then回调里的逻辑改成下面的写法,先不调用sendResponse,直接在setState的完成回调里打日志,看输出是否正常:
如果这里能拿到正确的返回值,说明setState本身没问题,问题出在后续的方法调用或者父组件逻辑里。.then(res => res.json()) .then(res => { console.log('接口原始返回:', res); this.setState({response: res}, () => { console.log('setState完成后的state.response:', this.state.response); // 这行log能拿到正确数据的话,再放开下面的sendResponse调用 // this.sendResponse() }) }) - 修复sendResponse的this指向问题
你写的sendResponse是类的普通方法,默认不会绑定组件实例的this,很容易出现指向丢失的问题。最省事的解决方式是直接把它改成类属性箭头函数,从根源避免this问题:
另外你写的loginSubmission本身就是类属性箭头函数,自带this绑定,构造函数里那句// 替换原来的sendResponse() { ... } sendResponse = () => { console.log(this.state.response) let data = {response: this.state.response}; this.props.receiveResponse(data); }this.loginSubmission = this.loginSubmission.bind(this);完全是多余的,可以直接删掉,不影响功能。 - 检查你判断“存储失败”的时机对不对
setState是异步操作,如果你是在fetch调用链的外面、loginSubmission方法里同步打this.state.response的日志,拿到的肯定是初始化的空对象——这时候请求还没回来,setState根本还没执行,不是存不上,是你打日志打早了。 - 排查父组件的receiveResponse逻辑
如果前面确认子组件里的state已经更新正确,那问题基本出在父组件:检查receiveResponse方法是不是没有调用setState存数据,是不是直接给this.state做赋值操作了,或者拿到数据之后又被其他逻辑重置成了初始值。 - 排查组件内的其他覆盖逻辑
检查输入框onChange事件、组件生命周期方法里有没有误写覆盖response字段的逻辑,比如改用户名密码的时候不小心把response也重置成空对象了。
最后提一句,你贴的测试代码里console.log('res.response')是打印固定字符串,如果你实际跑的代码也是这么写的,那控制台输出的根本不是接口返回值,记得改成console.log(res)再确认返回结构和你贴的一致。
内容的提问来源于stack exchange,提问作者Cy Lewis Howard
相关产品推荐
相关产品推荐

