Unable to set array in useState hook - javascript

This is my code i am trying to set array in setUsers the data comes properly from backend but it does not get set in hook please help me
const LoadAllUser = () => {
const [users, setUsers] = useState([]);
const loadUsers = () => {
loadalluser()
.then((data) => {
if (data.error) {
console.log(data.error);
// setusers(data.error);
} else {
setUsers(data);
}
})
.catch((eror) => console.log("error is here"));
};
useEffect(() => {
loadUsers();
}, []);
My return look like this
return (
<div>
{users.length > 0 ? (
users.map((index, user) => {
return (
<div key="index">
<label>Name : {user.name}</label>
<label>Email : {user.email}</label>
<label>NUmber : {user.number}</label>
</div>
);
})
) : (
<h1> No users found </h1>
)}
)
</div>
);
And this is how i fetch all of my users
export const loadalluser = (users) => {
return fetch(`${API}/loadalluser`, {
method: "GET",
body: JSON.stringify(users),
})
.then((response) => {
return response.json();
})
.catch((error) => {
return error;
});
};

fetch only rejects on cancelled requests or network errors, otherwise it returns a resolved Promise.
Checking that the fetch was successful
A fetch() promise will reject with a TypeError when a network error is
encountered or CORS is misconfigured on the server-side, although this
usually means permission issues or similar — a 404 does not constitute
a network error, for example. An accurate check for a successful
fetch() would include checking that the promise resolved, then
checking that the Response.ok property has a value of true.
You should check response.ok to ensure the request was successful or not. You can also likely also remove the catch block as any catch blocks in the consuming component code will catch them.
export const loadalluser = (users) => {
return fetch(`${API}/loadalluser`, {
method: "GET",
body: JSON.stringify(users),
})
.then((response) => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
});
};
This change should help by not silently swallowing failed fetch requests and update state with bad values.
Also, do you need to pass users to loadalluser for the body of the GET request?

Related

Can't call fetch api multiple times

I want to call this api multiple times in my project and when I am calling it , It continues giving an error which is
TypeError: Failed to execute 'json' on 'Response': body stream already
read at main.js:Line number
My Code is as Follows
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
a.then((data) => {
return data.json()
}).then((apidata) => {
console.log(apidata)
return apidata
}).catch((error) => {
console.log(error)
})
a.then((fetchdata) => {
return fetchdata.json()
}).then((readingData) => {
console.log(readingData)
}).catch((err) => {
console.log(err)
})
You're not calling fetch multiple times. You're calling it once, and then trying to read the response body multiple times. That's why the error says you're trying to read the body when the stream is already closed — it was closed when you were done reading it the first time.
If you want to use the data twice, store it somewhere and use it twice.
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
a.then((data) => {
return data.json()
}).then((apidata) => {
// **************** Use it twice here
}).catch((error) => {
console.log(error)
})
If you want to fetch it again because it may have been updated, call fetch again:
let thisIsUrl = 'https://api.covid19api.com/summary';
fetch(thisIsUrl)
.then((data) => {
return data.json();
}).then((apidata) => {
console.log(apidata);
return apidata;
}).catch((error) => {
console.log(error);
});
// Presumably this is later (in time), not immediately after the above
fetch(thisIsUrl)
.then((fetchdata) => {
return fetchdata.json();
}).then((readingData) => {
console.log(readingData);
}).catch((err) => {
console.log(err);
});
Finally, this seems unlikely, but if you really want to fetch it once and use that one result in multiple places via the promise chain, keep the promise from then rather than the promise from fetch:
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
.then((data) => {
return data.json()
});
a.then((apidata) => {
// ***** Use it here
}).catch((error) => {
console.log(error)
})
a.then((readingData) => {
// ***** And then again here
}).catch((err) => {
console.log(err);
});
Side note: Your code is falling prey to a footgun in the fetch API; I've written about it in this blog post. fetch only rejects its promise on network errors, not HTTP errors. You have to check for those yourself in the first fulfillment handler, by checking for ok on the response object:
fetch("/your/resource")
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status); // Or better, use an Error subclass
}
return response.json();
})
// ...
fetch returns Promise, generally, promises have something like state inside themself;
pending: initial state, neither fulfilled nor rejected.
fulfilled: meaning that the operation was completed successfully.
rejected: meaning that the operation failed.
(source)
So when we call them and get the value from them with then, catch and etc. then they change the state after that call. So here, when you read the value with a.then(…, the promise changes its state to fulfilled and you are not able to call it again, you need a new and fresh Promise, actually a new instance of the fetch.
I want to recommend you to use Promise.all().
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
.then((data) => {
return data.json()
}).then((apidata) => {
console.log(apidata)
return apidata
}).catch((error) => {
console.log(error)
})
Promise.all([a,a,a]);
.then(results => {
// Results are here.
});

How do I catch and parse errors properly in my React form submission handler?

I'm using React 16.13.0. I have the following function to deal with submit events:
handleFormSubmit(e) {
e.preventDefault();
const NC = this.state.newCoop;
delete NC.address.country;
fetch('/coops/',{
method: "POST",
body: JSON.stringify(this.state.newCoop),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
}).then(response => {
if (response.ok) {
return response.json();
}
console.log(response.json());
console.log(response.body);
throw new Error(response.statusText);
}).catch(errors => {
console.log(errors);
this.setState({ errors });
});
}
However, I'm having an issue getting the errors properly from the response. When an error occurs, my endpoint returns a 400 request with the error text. This is what happens in curl:
curl --header "Content-type: application/json" --data "$req" --request POST "http://localhost:9090/coops/"
{"phone":["The phone number entered is not valid."]}
But the response.statusText contains "400 (Bad Request)". What's the right way to catch the error text and preserve it for future parsing? If my endpoint needs to format the data differently, what should it do (using Django/Python 3.7)?
Edit:
This is the Input component in which I'm trying to display errors:
<Input inputType={'text'}
title = {'Phone'}
name = {'phone'}
value = {this.state.newCoop.phone}
placeholder = {'Enter phone number'}
handleChange = {this.handleInput}
errors = {this.state.errors}
/>
And the code of the input component, src/Input.jsx
import React from 'react';
import {FormControl, FormLabel} from 'react-bootstrap';
const Input = (props) => {
return (
<div className="form-group">
<FormLabel>{props.title}</FormLabel>
<FormControl
type={props.type}
id={props.name}
name={props.name}
value={props.value}
placeholder={props.placeholder}
onChange={props.handleChange}
/>
{props.errors && props.errors[props.name] && (
<FormControl.Feedback>
<div className="fieldError">
{props.errors[props.name]}
</div>
</FormControl.Feedback>
)}
</div>
)
}
export default Input;
When I run console.log(errors) they appear as such:
{phone: Array(1), web_site: Array(1)}
The Response.ok property API states that:
Response.ok Read only
A boolean indicating whether the response was successful (status in
the range 200–299) or not.
That means that even response.ok is false, the response.json() will return the data.
Body.json()
Takes a Response stream and reads it to completion. It returns a
promise that resolves with the result of parsing the body text as
JSON.
So, in your code, you should define your first fetch resolve to asynchronous and if the response it's not ok, then throw with the resolved response.json() using await:
handleFormSubmit(e) {
e.preventDefault();
const NC = this.state.newCoop;
delete NC.address.country;
fetch('/coops/',{
method: "POST",
body: JSON.stringify(this.state.newCoop),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
}).then(async response => { // Define the first resolve to an asynchronous function
if (response.ok) {
// If it's OK, resolve JSON and return the actual data
return await response.json();
// or better set react state
// const data = await response.json();
// this.setState({ data });
} else {
// It's not OK, throw an error with the JSON data
// so you'll be able to catch
throw await response.json();
}
}).catch(errors => {
// Here you should get the actual errors JSON response
console.log(errors);
this.setState({ errors });
});
}
You can check the test example working using fetch-mock in this Stackblitz workspace.
If my endpoint needs to format the data differently, what should it do
(using Django/Python 3.7)?
You'll have to let us know more about how your endpoint handles the requests by providing some code and explanation.
UPDATE
Regarding your component and displaying the errors, the result JSON returns an array of errors for each field. If you'll only one error, then change the endpoint to return a string instead of an array or display just the first error. If you'll have multiple errors, then you can map and render through all errors array for each field:
const Input = (props) => {
return (
<div className="form-group">
<FormLabel>{props.title}</FormLabel>
<FormControl
type={props.type}
id={props.name}
name={props.name}
value={props.value}
placeholder={props.placeholder}
onChange={props.handleChange}
/>
// If you just want to display the first error
// then render the first element of the errors array
{props.errors && props.errors[props.name] && (
<FormControl.Feedback>
<div className="fieldError">
{props.errors[props.name][0]}
</div>
</FormControl.Feedback>
)}
// Or if you may have multiple errors regarding each field
// then map and render through all errors
{/*
{props.errors && props.errors[props.name] && (
<FormControl.Feedback>
{props.errors[props.name].map((error, index) => (
<div key={`field-error-${props.name}-${index}`} className="fieldError">
{error}
</div>
))}
</FormControl.Feedback>
)}
*/}
</div>
)
}
Actually, the fetch API is a little different from others. you should pass another .then() to the get the data and then analyze it, and using several callbacks make codes hardly to read, I use async/await to handle the error:
async handleFormSubmit(e) {
e.preventDefault();
const NC = this.state.newCoop;
delete NC.address.country;
try {
const response = await fetch('/coops/',{
method: "POST",
body: JSON.stringify(this.state.newCoop),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
});
if (response.ok) {
const result = await response.json();
console.log('_result_: ', result);
return result;
}
throw await response.json();
} catch (errors) {
console.log('_error_: ', errors);
this.setState({ errors });
}
}
Update for your new question:
Definitely, it is another question, that why the error doesn't appear, in fact, the phone error is and JavaScript Array, and you should show it by its way like below code, I use restructuring assignment for props:
import React from 'react';
import { FormControl, FormLabel } from 'react-bootstrap';
const Input = ({
title,
type,
name,
value,
placeholder,
handleChange,
errors,
}) => (
<div className="form-group">
<FormLabel>{title}</FormLabel>
<FormControl
type={type}
id={name}
name={name}
value={value}
placeholder={placeholder}
onChange={handleChange}
/>
{errors && errors[name] && (
<FormControl.Feedback>
{errors[name].map((err, i) => (
<div key={err+i} className="fieldError">{err}</div>
))}
</FormControl.Feedback>
)}
</div>
);
export default Input;
You are not really explaining what you want to do with the response. But based on your use for throw new Error I assume you want the following .catch call to handle it. In the below solution, errors would be assigned the object from the JSON response.
response.json() returns a promise. If you want to "throw" that value as an error you have to do something like this (instead of throw new Error(...)):
return response.json().then(x => Promise.reject(x))
Returning a rejected promise from a .then callback causes the promises returned by said .then call to be rejected as well.
In context:
fetch('/coops/',{
method: "POST",
body: JSON.stringify(this.state.newCoop),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
}).then(response => {
if (response.ok) {
return response.json();
}
return response.json().then(x => Promise.reject(x));
}).catch(errors => {
console.log(errors);
this.setState({ errors });
});
Note: Since you are not doing anything with the return value of a successful response, the return response.json() inside the if statement is not necessary. You could rewrite that call to:
.then(response => {
if (!response.ok) {
return response.json().then(x => Promise.reject(x));
}
})
If my endpoint needs to format the data differently, what should it do (using Django/Python 3.7)?
Since we don't know what structure your component expects there is not much we can suggest. –
let the catch part catch failure errors and then catch the errors you need in the then part
}).then(response => {
//use a switch to got through the responses or an if statement
var response = JSON.Parse(response)
//here am assuming after you console log response you gt the correct array placement
if(response[0]==200){
// this indicates success
return JSON.stringify({"success":"true","message":"success message";})
}else if(response[0]==400){
// this indicates error
return JSON.stringify({"success":"false","message":"Error message";})
}
}).catch(errors => {
console.log(errors);
this.setState({ errors });
});
on the other end you could check the first entry in the array to determine whether the query was success or failure , then handle message appropriately
With the fetch API Response objects represent the Response meta - connection information, headers, that kind of thing.
At this stage the body has not been read.
response.statusText is the from the HTTP Response Status line, eg 200 Ok or 400 (Bad Request) in your case.
response.ok is a helper for checking if response.status >= 200 && response.status < 300
So far nothing we have looked at here is the contents of the response.
For that you need to use response.json(), response.text(), or response.blob(). As long as there is a body any of these functions can be used to read it - regardless of the HTTP Status Code.
Each of these functions read the body of sent to you by the server and does some kind of additional processing on it (check MDN or your preferred source of documentation for more information).
So in your case - to read and do something with the error returned to your by the server I would look at something like
fetch(...)
.then(response => {
if (response.ok) {
return response.json();
}
if (response.status === 400) {
return Promise.reject(response.json());
}
return Promise.reject(new Error(`Unexpected HTTP Status: ${response.status} - ${response.statusText}`));
})
.then(...)
.catch(...);

How to deal with Node error 'Unhandled promise rejection' so that page gets loaded anyway?

I'm creating a Next.js app served by a Node (Express) server which pulls in data through requests to an API. I keep my request endpoints in a separate api file:
const apiBase = 'http://www.example.com'
export default {
news: apiBase + '/news/'
// other endpoints
}
Then I do my requests in getInitialProps, and do conditional rendering based on whether the request gives an error or not:
static async getInitialProps( { query: { slug } } ) {
const news = await asyncReq( api.news + slug )
return { news, error: news.status }
}
render() {
return this.props.error ? <Error /> : <News />
}
asyncReq is a helper function that looks like this:
export const asyncReq = endpoint => {
return
fetch( endpoint )
.then( res => { return res.ok ? res.json() : res } )
}
This all works fine both when the request is successful and when I get 404 or 500 errors. But suppose I intentionally use a wrong endpoint:
const apiBase = 'http://www.example.com'
export default {
news: wrongApiBase + '/news/'
// other endpoints
}
In this case, Node gives me the following error because wrongApiBase is undefined:
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 498)
which is what it should do, but the error causes the page to never get loaded. What am I supposed to do to handle the error? My idea was to chain a catch statement to asyncReq, but I'm not sure what I should return from it that I can then use in getInitialProps. I tried returning false but nothing changes, the page just doesn't load.
export const asyncReq = endpoint => {
return
fetch( endpoint )
.then( res => { return res.ok ? res.json() : res } )
.catch( err => { // What should I return here? )
}
+++ UPDATE +++
As it turns out, there was an issue with the error I was producing. Like I said, I was using a wrong variable name (wrongBaseApi) to trigger an error, which caused Node to never serve the page. In hindsight, it makes sense, as it's an error with Node code and not with the incoming request.
By using the right variable but assigning it a wrong value (an actually wrong API base, such as http://dahfjkdahfds.com, which is a not a Node error but an error with the request), I was able to make the solution of using a try/catch block offered by #iKoala and #DEVCNN work. So my code became:
static async getInitialProps( { query: { slug } } ) {
const news = await asyncReq( api.news + slug )
return { news }
}
render() {
// this.props.news.data
// this.props.news.error
}
and
export async function asyncReq( endpoint ) {
try {
const res = await fetch( endpoint )
return {
data: res.ok ? await res.json().then( val => { return val } ) : null,
error: res.ok ? false : res.status
}
} catch( error ) {
return { error }
}
}
I think you have to handle the error thrown from asyncReq:
static async getInitialProps( { query: { slug } } ) {
try {
const news = await asyncReq( api.news + slug )
return { news, error: news.status }
} catch (err) {
// any error, including http error 40x and 50x
return { news: null, error: err };
}
}
Not a good approach but we can achieve by adding an event listener on the root node
window.addEventListener('unhandledrejection', rj => {
this.setState({hasError: true})
})
something like this.
Since it's NodeJS, I would use process.on() (instead of window.addEventListener()) with the unhandledRejection event:
process.on("unhandledRejection", (err, promise) => {
console.log(`Unhandled rejection (promise: ${promise}, reason: ${err})`);
});
Let's say your getInitialProps calls asyncReq which in turn calls throwError method. Now if throwError method throws an error, you can catch it with the catch block in getInitialProps. So you should always put a catch block in the function that starts the function chain. To diagnose errors better, you should put the catch block in each function. But as a general rule, catch block in the first function that you call is a must.
getInitialProps = async function( { query: { slug } } ) {
try{
const news = await asyncReq( 'http://localhost:3000/' + slug )
return { news, error: news.status }
}
catch(err){
console.log('err:', err);
}
}
const throwError = () => {
throw 'New Error'
}
const asyncReq = endpoint => {
throwError();
return
fetch( endpoint )
.then( res => { return res.ok ? res.json() : res } )
}

How to make multiple API requests in a Promise in a Redux action

I'm working with React/Redux in a project and I need to make 2 separate API requests, but the second request depends on the first returning without any issues. In the below example of an action, I'm trying to wrap both calls in a promise, but it's not quite working (getting a Uncaught Error: Actions must be plain objects. Use custom middleware for async actions. error in the console). I don't necessarily need to do anything with the response data as a result of both calls. I just need them to return a 200 status or an error.
Note: Unfortunately, I can't use async/await for this example. Thanks!
export default () => {
const url = '/api';
const options = {...}
const otherOptions = {...}
return new Promise((resolve, reject) => {
return dispatch =>
// First API request
axios.post(url, options)
.then(responseA => dispatch({ type: RESPONSE_A_SUCCESS }))
.then(() =>
// Second separate API request
axios.post(url, otherOptions)
.then(responseB => {
dispatch({ type: RESPONSE_B_SUCCESS });
resolve();
})
)
.catch(error => {
dispatch({ type: errorActionType, error: error });
reject(error);
});
});
};
Your code has 2 issues:
It returns a promise, which is not a "plain object".
You are nesting promises instead of attaching them sequentally
Try this instead:
export default () => {
const url = '/api';
const options = {...}
const otherOptions = {...}
return dispatch =>
axios.post(url, options)
.then(responseA => dispatch({ type: RESPONSE_A_SUCCESS }))
.then(() => axios.post(url, otherOptions))
.then(responseB => dispatch({ type: RESPONSE_B_SUCCESS }))
.catch(error => {
dispatch({ type: errorActionType, error: error });
reject(error);
});
});
};

react native fetch not calling then or catch

I am using fetch to make some API calls in react-native, sometimes randomly the fetch does not fire requests to server and my then or except blocks are not called. This happens randomly, I think there might be a race condition or something similar. After failing requests once like this, the requests to same API never get fired till I reload the app. Any ideas how to trace reason behind this. The code I used is below.
const host = liveBaseHost;
const url = `${host}${route}?observer_id=${user._id}`;
let options = Object.assign({
method: verb
}, params
? {
body: JSON.stringify(params)
}
: null);
options.headers = NimbusApi.headers(user)
return fetch(url, options).then(resp => {
let json = resp.json();
if (resp.ok) {
return json
}
return json.then(err => {
throw err
});
}).then(json => json);
Fetch might be throwing an error and you have not added the catch block. Try this:
return fetch(url, options)
.then((resp) => {
if (resp.ok) {
return resp.json()
.then((responseData) => {
return responseData;
});
}
return resp.json()
.then((error) => {
return Promise.reject(error);
});
})
.catch(err => {/* catch the error here */});
Remember that Promises usually have this format:
promise(params)
.then(resp => { /* This callback is called is promise is resolved */ },
cause => {/* This callback is called if primise is rejected */})
.catch(error => { /* This callback is called if an unmanaged error is thrown */ });
I'm using it in this way because I faced the same problem before.
Let me know if it helps to you.
Wrap your fetch in a try-catch:
let res;
try {
res = fetch();
} catch(err) {
console.error('err.message:', err.message);
}
If you are seeing "network failure error" it is either CORS or the really funny one, but it got me in the past, check that you are not in Airplane Mode.
I got stuck into this too, api call is neither going into then nor into catch. Make sure your phone and development code is connected to same Internet network, That worked out for me.

Categories