ОК, забудьте про обратные вызовы, я там уже был, не более ЗВОНОК АД .
Всегда используйте обещания, и вы можете упростить все, используя 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
}
}
в вашем Реактивном Компоненте:
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'
}
}
Теперь ваш код выглядит более читабельным!
Ошибки извлечения находятся либо в result.error, либо в result.statusText, я давно перестал использовать fetch, переключился на Axios . Посмотрите на мой ответ о некоторых различиях между 2 Здесь .
РЕДАКТИРОВАТЬ НА ОСНОВЕ ВАШЕГО ОТВЕТА
Хорошо, на основании кода, который вы разместили:
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
.
Теперь, исходя из предоставленного вами кода, мне кажется, что вы находитесь на неизведанной территории. Если вам кажется, что вы находите маршрутизацию сложной или асинхронный / ожидающий неясен, я советую вам не использовать их и сначала освоить основы React (у вас есть проблема с синтаксисом, например, вы не должны поместите эту функцию туда, а также проблему связывания с this
).
Прочтите, например, в этом посте , чтобы иметь общее представление, и я также предлагаю вам попробовать другие более простые примеры перед использованием async, fetch или routing. Когда вы очистите жизненный цикл React, вы сможете продолжить с него и использовать асинхронные функции, а затем маршрутизаторы.
Я также предлагаю вам следовать примерам в Официальных документах , а также взглянуть на этот пост , чтобы лучше понять async / await.
Эти предложения, конечно, даются, чтобы вы могли освоить React с четкими основами, и в будущем не будет никаких проблем с основами! :)