【问题标题】:history.push not working in fetch callbackhistory.push 在获取回调中不起作用
【发布时间】:2019-02-20 17:56:23
【问题描述】:

我正在开发简单的 react js 应用程序,我正在验证用户,如果他/她已成功登录,我正在尝试重定向到主页,但我处于一些奇怪的情况。请帮助我完成以下代码。

下面是函数fetchAPI 使用一些输入参数调用服务器的代码

function fetchAPI(methodType, url, data, callback){

    fetch(url,{
        method: methodType,
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)  
    })
    .then(response => response.json())
    .then(data => callback(data) )
    .catch(error => callback(data));  

}

现在我这样称呼它

fetchAPI("POST", Constants.LOGIN, data, function(callback) {
        if(callback.status == 200) {
            console.log(callback.message);
            this.props.history.push("/home");
        }else if( typeof callback.status != "undefined"){
            alertModal("Alert", callback.message);
        }
      });

这个问题是它没有重定向到/home作为响应条件中的提及,但只打印成功消息。 但是当我像下面的代码一样直接使用 fetch api 时,它会将我重定向到/home

谁能帮我看看为什么会发生在我身上??

fetch(Constants.LOGIN, {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(data)
      })
        .then(response => response.json())
        .then(data => {
          if (data.status == 200) {
            this.props.history.push("/home");
          } else if (typeof data.status != "undefined") {
            alertModal("Alert", data.message);
          }
        })
        .catch(error => callback(data));

【问题讨论】:

  • 在您的fetchAPI 通话中,this 似乎断章取义。日志或控制台中是否有任何错误?
  • 不,除了console.log(callback.message);

标签: reactjs callback fetch fetch-api


【解决方案1】:

好的,忘记回调,我去过那里,不再有CALLBACK HELL

始终使用 Promise,您可以使用 async/await 来简化一切:

async function fetchAPI(methodType, url, data){
    try {
        let result = await fetch(url, {
            method: methodType,
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data)  
        }); // wait until request is done
        let responseOK = response && response.ok;
        if (responseOK) {
            let data = await response.json();
            // do something with data
            return data;
        } else {
            return response;
        }
    } catch (error) {
        // log your error, you can also return it to handle it in your calling function
    }
}

在你的 React 组件中:

async someFunction(){
    let result = await fetchAPI("POST", Constants.LOGIN, data); // wait for the fetch to complete
    if (!result.error){
        // get whatever you need from 'result'
        this.props.history.push("/home");
    } else {
        // show error from 'result.error'
    }
}

现在您的代码看起来更具可读性!

fetch 的错误在 result.error 或 result.statusText 中,我很久以前就停止使用 fetch,切换到 Axios。看看我对 2 Here 之间的一些差异的回答。

根据您的回复进行编辑

好的,根据您发布的代码:

import React from "react";
import Constants from "../Constants.jsx";
import { withRouter } from "react-router-dom";

class Login extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      email: "",
      password: "",
      errors: []
    };
  }

  showValidationErr(elm, msg) {
    this.setState(prevState => ({
      errors: [...prevState.errors, { elm, msg }]
    }));
  }

  clearValidationErr(elm) {
    this.setState(prevState => {
      let newArr = [];
      for (let err of prevState.errors) {
        if (elm != err.elm) {
          newArr.push(err);
        }
      }
      return { errors: newArr };
    });
  }

  onEmailChange(e) {
    this.setState({ email: e.target.value });
    this.clearValidationErr("email");
  }

  onPasswordChange(e) {
    this.setState({ password: e.target.value });
    this.clearValidationErr("password");
  }

  submitLogin(e) {
    e.preventDefault();

    const { email, password } = this.state;
    if (email == "") {
      this.showValidationErr("email", "Email field cannot be empty");
    }
    if (password == "") {
      this.showValidationErr("password", "Password field cannot be empty");
    }

    if (email != "" && password != "") {
      var data = {
        username: this.state.email,
        password: this.state.password
      };


        // I added function keyword between the below line
        async function someFunction(){
          let result = await fetchAPI("POST", Constants.LOGIN, data); // wait for the fetch to complete
          if (!result.error){
              this.props.history.push("/home");  // Here is the error
          } else {
              // show error from 'result.error'
          }
        }
        someFunction();
    }


  }

  render() {  ......................

####-----This is function definition------####

async function fetchAPI(methodType, url, data){
    try {
        let response = await fetch(url, {
            method: methodType,
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data)  
        }); // wait until request is done
        let responseOK = response && response.ok;
        if (responseOK) {
            let data = await response.json();
            // do something with data
            return data;
        } else {
            return response;
        }
    } catch (error) {
        return error;
        // log your error, you can also return it to handle it in your calling function
    }
}

这就是想法,您应该将async 设为调用API 的函数。在您的示例中,您的函数 submitLogin 必须是异步的,因为它将在内部调用异步函数。只要您调用异步函数,调用者必须是异步的,或者相应地处理承诺。应该是这样的:

  async submitLogin(e) {
    e.preventDefault();

    const { email, password } = this.state;
    if (email == "") {
      this.showValidationErr("email", "Email field cannot be empty");
    }
    if (password == "") {
      this.showValidationErr("password", "Password field cannot be empty");
    }

    if (email != "" && password != "") {
      var data = {
        username: this.state.email,
        password: this.state.password
      };

      let result = await fetchAPI("POST", Constants.LOGIN, data); // wait for the fetch to complete
      if (!result.error) {
        this.props.history.push("/home");  // Here is the error
      } else {
        // show error from 'result.error'
      }
    }

如果函数在构造函数中正确绑定,this 不会有任何问题。您似乎没有在构造函数中绑定submitLogin 函数,这会给您带来this 的上下文问题。应该是这样绑定的:

constructor(props) {
    super(props);
    this.state = {
      email: "",
      password: "",
      errors: []
    };

    // bind all functions used in render
    this.submitLogin = this.submitLogin.bind(this);
  }

查看this article 以了解有关this 上下文问题的更多信息。

现在,根据您提供的代码,在我看来,您处于未知领域。如果你觉得路由很难或者 async/await 不清楚,我建议你不要使用它们,先掌握 React 基础知识(你遇到的语法问题是一个例子,你不应该有把那个函数放在那里,还有this的绑定问题)。

例如,阅读this post 以了解总体思路,我还建议您在使用异步、获取或路由之前尝试其他更简单的示例。当你清楚 React 生命周期后,你可以从那里继续,使用异步函数,然后是路由器。

我还建议您按照Official docs 中的示例,同时查看at this post 以更好地了解async/await。

这些建议当然是为了让你能够以清晰的基础掌握 React,并且在未来的基础上不会有任何问题! :)

【讨论】:

  • 好的,感谢您的回复,我一定会试试这个。
  • 嘿抱歉,这里的调用函数代码中有语法错误async someFunction(){
  • 您的答案几乎没有语法错误,我已在代码中更正。现在他们在我的控制台this is undefined中出现了这个错误this.props.history..
  • @AbhiBurk 如果在 React 组件中使用该函数,async someFunction(){ 中没有语法错误。此外,只要您正确使用路由器,this 应该不会有任何问题。您如何使用我提供的解决方案?你能把你的最终代码上传到小提琴或你原来的问题吗?
  • 我可以上传到某个地方
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-15
  • 2017-02-23
  • 2021-11-02
  • 1970-01-01
  • 2021-08-05
  • 1970-01-01
  • 2021-03-11
相关资源
最近更新 更多