setState not firing inside axios POST request error handler - javascript

I am trying to set up an error handler for a React Component SignupForm.js that is responsible for registering users. In particular, the handler I am focused on implementing involves handling instances of a supplied user email already in use.
Here is my POST request:
await axios
.post("http://localhost:8000/signup", {
name,
email,
password,
tokenValue,
})
.catch((error) => {
console.log("ERROR --> ", error);
//this.displayError(error);
this.setState(
{
err: true,
errcode: error.response.data.errcode,
errtext: error.response.data.errText,
},
() => {
console.log("setstate done");
}
);
})
.then((res) => {
if (res !== undefined) {
console.log(res.data);
}
});
The image below shows Chrome Dev Tools output:
As you can see, console.log("Error --> ", error); is indeed firing.
However, this.setState() doesn't seem to be firing. There are a few indications of this.
The SignupForm state, as observed using the React DevTools plugin, remains unchanged.
I have included a print statement at the very top of my render() function like so:
render() {
console.log(this.state);
return (
<div className="form-container">
<form
name="sign-up"
method="post"
className="form-sign-up"
despite the notion that setState() calls are supposed to trigger re-renders, the component state never gets printed to the console after the error being caught.
console.log("setstate done"); never fires, as the output is also missing from the DevTools console.
Things I have tried
I tried encapsulating the setState() call into a error handler function displayError(e), like so:
displayError = (e) => {
this.setState(() => ({
err: true,
errcode: e.response.data.errcode,
errtext: e.response.data.errText,
}));
};
This didn't seem to have any effect. I also tried calling displayError(e) after having called this.displayError = this.displayError.bind(this); in the component constructor, which similarly had no effect.
At this point, I'm not really sure what else to try.

Figured it out. Initially, I was quite set on trying to get my code posted online for you folks to take a look at. After getting to know CodeSandbox a bit, I thought I was going to need to learn Docker so I could compose the front-end and back-end while having a free MySQL DB hosted somewhere online.
This was discouraging to me, as I knew Docker might take a bit of time to learn and I wanted to get to the bottom of this with the least amount of hoops possible.
I took what #S.Marx said and I investigated. After thinking about what they said a little more, it seemed strange to me as well that I would put my catch block before my then block.
Sure enough, swapping those two blocks completely solved the issue. The state is now being changed with the following:
await axios
.post("http://localhost:8000/signup", {
name,
email,
password,
tokenValue,
})
.then((res) => {
if (res !== undefined) {
console.log(res.data);
console.log("Signup Successful!!");
}
})
.catch((error) => {
console.log("Signup Error! --> ", error);
//this.displayError(error);
this.setState(
{
err: true,
errcode: error.response.data.errcode,
errtext: error.response.data.errText,
},
() => {
console.log("setstate done");
}
);
});
Of course, now the component that is supposed to render the state change on-screen isn't showing the error message. That, however, is a separate issue.

Related

JavaScript the rest of function executes before API calls

I have a problem with a simple function which where I want to call an API and then do something with the response. Basically, I just want to set my react component state to the response I get and then navigate to the other page The problem is that my code executes another part of the function before an API call is finished, and I end up on another page with console.log of undefined
There is my function:
const startNewGame = () => {
GameService.startGame()
.then((response) => {
setGame(response.data);
console.log(game);
navigate('intro');
})
.catch((e) => {
console.log(e);
});
};
I can wrap my navigate into if(!game !== undefined) but then I have to click two or more times on a button.
Thank you all guys for help :)
You probably do several things incorrect at the same time, so to understand what exactly is wrong might take some time. Take these steps to debug your code:
Make each .then call do only one thing (and stick to that principle in other cases). You could chain as many .then as you like. Moreover you could return data from one .then to the next, so your code might look like this:
GameService.startGame()
.then(response => response.data)
.then(data => {
setGame(data)
})
.then(() => {
console.log(game);
navigate('intro');
})
.catch((e) => {
console.log(e);
});
Understand your components composition. Where exactly are you saving your response? is it just local component useState or some Context Api state that wraps the app? When you navigate to "other page" your "current page" state will be unavailable to "other page" unless you keep the data somewhere up in the tree when both pages could access that.
For further references keep in mind that setGame is asynchronous, so you need to wait for the state to be updated to make sure that its updated.
Try this:
const startNewGame = () => {
GameService.startGame()
.then((response) => {
setGame(response.data);
// game is not updated yet
// console.log(game); <- Remove this line
// navigate('intro'); <- Remove this line
})
.catch((e) => {
console.log(e);
});
};
useEffect(()=>{
if(game !== undefined){
navigate('intro')
}
}, [game])

Reusing Fetch with Different Pieces of State in React (Flux)

I have this function that is in the top level component that I'd like to reuse but on a different piece of state. In the following code sample, I'm using it on userData, but I'd like to be able to reuse it on a piece of state called repoData, either within the same component it's child, or outside. Just not sure how to go about it, since, if I do something like pass it the states as args, setting the state would throw an error because it would look like: this.setState({this.state.userData: data}).
fetchUserData(params) {
fetch('https://api.github.com/users/' + params)
.then(response => {
if (!response.ok) {
throw Error("Network failure")
}
return response;
})
.then(data => data.json())
.then(data => {
this.setState({
userData: data
})
}, () => {
this.setState({
requestFailed: true
})
})
}
Your question is more about refactoring your code I believe. Here you can look at it as moving out the logic of fetching user data asynchronously. Your component need not know who(fetch, axios) is doing the work of fetching the data for it. Also it is not required for a component to know from where(https://api.github.com/users/' + params) to get this data.
One possible way of doing it is to move your fetch call in a separate function which will return the data to your component after fetching it asynchronously. Then it is responsibility of component to handle the data the way it want to.
So as you can see the key point here is identifying and separating out the responsibilities of each piece of code. Advantages of doing this will be clean code structure, less number of lines, maintainable code and most importantly easily testable code.
I hope this solves your problem. If you are interested to learn about such techniques more you can read about DRY principle and refactoring techniques.
Edit 1:
I have created this code sample for your reference
What are JavaScript Promises: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Some samples for fetch: https://davidwalsh.name/fetch
I personally recommend using axios. You can see comparison of fetch and axios here: https://medium.com/#thejasonfile/fetch-vs-axios-js-for-making-http-requests-2b261cdd3af5
If I'm understanding you correctly
you should move the function to a file x.js
it should accept an other param which is the name of the new (piece) of state
it should also accept a param for the this.setState of the component calling it.
import it and use it in componentDidMount as normal
```
export const fetchUserData(params,setState , stateParam) {
fetch('https://api.github.com/users/' + params)
.then(response => {
if (!response.ok) {
throw Error("Network failure")
}
return response;
})
.then(data => data.json())
.then(data => {
setState({
[stateParam]: data
})
}, () => {
setState({
requestFailed: true
})
})
}
you can call it as follows
import { fetchUserDate } from "./path/to/x"
fetchUserDate("param" , this.setState, "xOnState")

redux error handling for fetchs

I'm working on error handling w/ fetch's in redux. Do people typically write reducers/actions error handlers for every single request like the videos from redux.js.org? I was thinking about dispatching the same action w/ an error flag to handle errors, but I don't know if that's the right way to go about it.
In my experience, yes. I ordinarily have actions like GET_THINGS, GET_THINGS_SUCCESS, and GET_THINGS_ERROR, and then have a case statement in the reducer to handle each action. Most React devs I know do it the same well. In general, I would suggest following that pattern. That said, it does result in you having to handle a lot of GET actions and ERROR actions basically the same way, since you'll likely just be adding something like { Loading: true } or { Error: true } to the state...
For error handling and posting / putting data I use a generic way of raising a dialog for the user to indicate success or failure.
e.g. have a NoificationsAction
const NotificationAction = (open = false, message = '', status = 'success') => ({type: NOTIFICATION, payload: {open, message, status}})
and some dilaog component which raises a dialog or snackBar to display a message
Dispatch this when your request fails
myRequest()
.then((response) => {
if (response.status === 200) {
dispatch(projectsSuccess(response.data))
}
})
.catch((error) => {
dispatch(NotificationAction(true, `My Error Text : ${error}`, 'error'))
})
This way you reduce your boiler plate code in reducers

React Native Fetch does not render response until after clicking screen

So I've been building an app that uses the Fetch API to make a request. Unfortunately, I think the code shown in the Facebook docs are (may be?) incorrect.
Problem
When starting the app, Fetch makes a call to a URL to pull JSON data.
componentDidMount(){
return fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
data: JSON.stringify(responseJson)
})
})
.catch((error) => {
console.error(error);
});
}
This is similar to the code that Facebook docs give as an example.
The problem is that when I start the app, the data doesn't render in the render function.
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
var data = this.state.data;
return this.renderData(data);
}
At least, it doesn't render until after I click/touch the screen. Once I touch the screen, the data renders. Otherwise, it just stays in a perpetual "loading" state.
Solution?
I remembered that sometime in the past, I had to bind event handlers to their respective controls (this.handleClick = this.handleClick.bind(this), for example)
And that got me thinking: Where is this in the promise request? Where does this point to?
componentDidMount(){
return fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseJson) => {
this.setState({ <--- **Where are you going?**
isLoading: false,
data: JSON.stringify(responseJson)
})
})
I console.logged "this" within the responseJson promise, and it does point to the component, but I'm not convinced. With it being buried inside of the promise, I don't think the this.setState function is actually able to set the component's State.
So I made a variable before the fetch request.
const that = this;
return fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseJson) => {
that.setState({
isLoading: false,
data: JSON.stringify(responseJson)
})
})
Now I'm not a software guru. I've been teaching myself this stuff for the last couple of years, so my logic is off half the time.
So if my reasoning is correct or incorrect, please let me know! What I do know is that this is working for me; once the data is fetched, it automatically sets the state and reloads, showing me the JSON data when rendered.
Any thoughts? Am I completely off here? Am I missing anything?
I saw a problem like yours. It's not about your code. Are using the app in the Remote JS debugging mode? If so, disable it. If it didn't work, try to install a release version.
(Posted on behalf of the OP).
Apparently it was due to running the app in debug mode. Running the app with this.setState works just fine when not in debug mode.

Meteor how to wait on callback before dispatching Redux action

Meteor uses callbacks, but it seems like there is no 'wait' for them with Redux actions. So, if using something like Redux and having an action something like this:
export function loginWithPassword(email, password) {
return dispatch => {
Meteor.loginWithPassword(email, password, error => {
if (error) {
return dispatch({type: ASYNC_ERROR, data: error.reason})
});
}
}
... the action will complete and return before Meteor's call does (thus not re-rendering the UI if needed). I have scoured for hours trying to find the best way to deal with this to no avail. Based on what I have been able to find, it would seem the options are Meteor Futures or Promises, however I cannot find anything indicating which might be better or why.
I believe that Futures can only be server side and are less widely used than Promises, so Promises may be the better options based on that. I am not even sure if Futures is an option since actions are client side. But I am also not sure if Meteor will play nice with Promises given it's general sync nature.
Promises I believe could be either client or server. Assuming they were client, would it be better to put them in the actions or in the UI code dispatching the actions - or does it really matter?
Does anyone have any thoughts or insight into how best to handle something like this? A working example of one or the other (or both) would be great because I sure couldn't find one after hours of searching. Specifically it would be great to see an example of a simple Meteor login with password dispatched via Redux that displays a 'User not found" or some other 'async' error in the UI login form (the FIRST time).
The action above actually works, but it returns before Meteor is done returning the error, so the error is not displayed in the UI the initial time.
TIA!
P.S. There are a couple of examples out there that display the error in an alert window or console.log it - but that is not the same thing as updating/displaying it as a prop in the UI that dispatched the action. There are no prop re-renders tied to an alert or console.log. The whole idea is to show the error in the form the user is currently on (login form in this example case)
Using redux-thunk, you could write something like below. I think the trick here is knowing the status of the user object once it's logged in. One way of doing this is to use store.dispatch inside a Tracker.autorun:
// whenever the reactive data changes, we will dispatch the appropriate action
Tracker.autorun(() => {
if (Meteor.loggingIn()) {
return store.dispatch({
type: LOGGING_IN,
});
}
const user = Meteor.user();
store.dispatch({
type: SET_USER,
user,
}
});
export const login = ({ email, password }) => (dispatch) => {
Meteor.loginWithPassword(email, password, (error) => {
if (error) {
return dispatch({
type: LOGIN_ERROR,
error,
});
}
return dispatch({
type: LOGIN_SUCCESS,
});
});
};
You could imagine a user reducer that has a state like {user: {loggingIn: Boolean, authError: SomeErrorModel}}} or something like that
You could clear the authError on LOGGING_IN, and add it on LOGIN_ERROR
For any UI changes based on this reducer, just connect your react component with react-redux's connect()

Categories