data.json is not a function reactjs using Fetch API - javascript

I have delete function using fetch api but while deleting, I'm getting this error, data.json is not a function
This is my code
export const deleteLeaveType = async id => {
const response = await fetch(
`${API_LINK}/route/route/route/${id}`, {
method: 'delete',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${Auth.getToken()}`,
},
}
);
return getData(response);
};
I'm calling it in another file
const deleteLeaveType = async id => {
let deleteLeaveType = isLeaveType.filter(
item => item.id !== id
);
await CalendarManagementAPI.deleteLeaveType(id)
.then(data => {
console.log(data.json());
setLeaveType(deleteLeaveType);
});
setLoadingLeaveTypes(false);
fetchLeaveTypes();
})
.catch(error => {
console.log(error);
});
};

Main issue:
The main issue you're running into is that in your top deleteLeaveType function, you're ending with return getData(response), instead of just returning whatever you received from your fetch request.
As you can see from the docs, the normal way to use fetch is to just do const response = await fetch(...) (as you've already done), then immediately after call data.json() (which is another async function, by the way). However, your getData(response) function is probably not returning exactly what you expect.
Other comments:
As mentioned in the comments, you probably shouldn't be using async / await at the same time as .then(). These are two different ways of handling async functions, so only one should be used at a time.
In your deleteLeaveType function, if you prefer using .then to handle async functions you can get rid or async and await like this:
// remove 'async' from function declaration
const deleteLeaveType = id => {
// keep original code
// remove 'await'
CalendarManagementAPI.deleteLeaveType(id)
.then(data => {
// ... everything else the same
};
OR to use async / await the right way, like you used in the first deleteLeaveType in your question, do this:
const deleteLeaveType = async id => {
let deleteLeaveType = isLeaveType.filter(
item => item.id !== id
);
let data = await CalendarManagementAPI.deleteLeaveType(id);
// ... the rest of your code
};
Here's an interesting article explaining some ideas for error handling with async / await, if you're interested: Better error handling with async / await
I would also recommend not using the same variable name deleteLeaveType for two functions and an array. This can get a bit confusing as to what you're referencing at any given moment in time.

Related

Can't access lexical definition of response before initialization within a React hook

I'm trying to make the home page send one API call on load and display all results on screen. It seems to send the call and receive response fine, although despite receiving the response from the server it can't pass the contents of the payload within the code, which is a JSON.
useEffect(() => {
const localUser = localStorage.getItem("user");
if (localUser) {
const foundUser = localUser;
setUser(foundUser);
} else {
const newUser = uuidv1();
localStorage.setItem(newUser, user);
setUser(newUser);
}
console.log(user);
async function fetchPosts() {
try {
let tempPosts = [];
const response = await fetch('http://localhost:3000/posts')
.then(response => response.json())
.then(response.payload.forEach(object => tempPosts.push(object.post)))
.then(setPosts((posts) => [tempPosts]));
console.log(posts);
} catch (err) {
console.log(err)
}
}
fetchPosts();
}, [user, posts]);
Somehow React is trying to access the response without the declaration and I have no idea how, which in result stops the function from executing.
Take a look at this line:
const response = await fetch('http://localhost:3000/posts')
.then(response => response.json())
You're combining two paradigms - asynchronous programming using Promises and callbacks with then, and asynchronous programming using async/await. You'll usually want to pick one or the other for use in a single function, and you definitely cannot combine them in a single line (or at least, not like this).
If you want to use async (and I would recommend this approach), you'll probably want something like this:
async function fetchPosts() {
let tempPosts = [];
const response = await fetch('http://localhost:3000/posts');
const data = await response.json();
data.payload.forEach(object => tempPosts.push(object.post))
return tempPosts;
}
I don't know what data looks like, so you may have to play with the 4th line of the function, but hopefully you get the gist. You'll probably want to define this function outside of your component, and certainly not within the useEffect hook.
An example of how you could use this to fetch posts on the first render of your component is
useEffect(() => {
fetchPosts().then(data => setPosts(data));
}, []);
assuming you have a relevant useState hook.

Not able to use await in then block to call another API in react

I am calling one API.in the then section of API call, i am calling another API. the output of first API will be passed to another API.
await axios
.post(process.env + '/certificates/upload', {
"name": "Shruti"
}
})
.then((response: any) => {
filenames = JSON.stringify(response.data.office);
axios // Not able to write async here
.patch(process.env + "/certificates/", {
file: filenames
})
.then(function(response: any) {
alert(' Record updated successfully');
})
.catch((err: any) => {
alert('Error in updating the record');
});
I am not able to use await in second API call. Where should I put async to use await in second API call? first call is working properly. also Is there any better way to call consecutive API and Passing output of first call to second.
Find the function that contains the statement you wish to await. Add async to the beginning of that function.
More generally, avoid mixing async/await with chained .then/.catch.
I think this is what you want:
try {
let response1 = await axios.post(
process.env + '/certificates/upload',
{ name: "Shruti" }
)
let filenames = JSON.stringify(response1.data.office)
await axios.patch(
process.env + "/certificates/",
{ file: filenames }
)
alert(`Update succeeded`)
} catch( error ) {
alert(`Update failed`)
}

making second api call after getting data from the first api

I have this first API call which gets me some data and once I have got the data from this api , only then I need to make the second api call . It must happen in the series and not parallelly. How can we do this in react ComponentDidMount?
I am listening to firebase .
Let us suppose that the first api gets me a matchId and now we need to use this matchid to make second call just after first api call without any click .
Let us suppose that this is my first firebase call .
const cardsListener =
getDATA()
.onSnapshot( (doc)=>{
console.log("doc is ",doc);
let data = doc.data();
console.log("data",data);
this.setState({
datainstate:data
});
});
Thanks to async/await, you can wait until an action is done.
componentDidMount() {
this.firstApiCall()
}
async firstApiCall() {
await fetch("http://myurl.com", {
method: 'POST',
body: JSON.stringify(data), // data can be `string` or {object}!
headers:{
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then((responseJson) => {
//if it worked
this.secondApiCall()
})
.catch(error => console.error('Error:', error))
}
secondApiCall() {
}
There are many ways you could do this. My preferred way is to use async await. I'll give an example of how you would use this below.
const getData = async() => {
try {
const apiCall1 = await axios.get(SOME_URL);
const apiCall2 = await axios.get(SOME_URL/apiCall1.data.id)
return apiCall2
} catch(e) {
return e
}
}
It's a silly example but you hopefully get the point. I make my first API call and await the response. Then I make my second API call using some data from the first call then return. You can do whatever logic you want in-between that.
You could also use a callback or promise but I think async await is clearer, and generally less code.
Async Await Documentation - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Promises Documentation - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Callback Documentation - https://developer.mozilla.org/en-US/docs/Glossary/Callback_function
You can follow something like this:
componentDidMount() {
fetch("apiUrl").then(res => {
//Do whatever you want to do with the response or get the id
const {id} = res;
fetch(`apiUrl/${id}`).then(response => {
// Do anything with the response of the api call here
}).catch(error => {
//Thing to do on error
});
}).catch(err => {
//Thing to do on error of 1st api call
});
}

Async functions using value from a promise

So I know this question is asked a lot, but I'm trying to retrieve a variable that is created within a promise. The examples I've seen on here involve calling .then and using the data there, however what I'm trying to do involves an async function--which i cant use within the .then block.
Here's my code. I'm using the Asana API To call out a lists of tasks that are due. It successfuly logs it. But I want to save the list value from the last block as a variable that I can use elsewhere.
const asana = require('asana');
const client = asana.Client.create().useAccessToken("xxx");
client.users.me()
.then(user => {
const userId = user.id;
// The user's "default" workspace is the first one in the list, though
// any user can have multiple workspaces so you can't always assume this
// is the one you want to work with.
const workspaceId = user.workspaces[0].id;
return client.tasks.findAll({
assignee: userId,
workspace: workspaceId,
completed_since: 'now',
opt_fields: 'id,name,assignee_status,completed'
});
})
.then(response => {
// There may be more pages of data, we could stream or return a promise
// to request those here - for now, let's just return the first page
// of items.
return response.data;
})
.filter(task => {
return task.assignee_status === 'today' ||
task.assignee_status === 'new';
})
.then(list => {
console.log (util.inspect(list, {
colors: true,
depth: null
}));
})
.catch(e => {
console.log(e);
});
If you're open to rewriting your .then()'s as async/await something like this could work for you:
const fetch = require('node-fetch');
async function doit() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const json = await response.json();
console.log(json);
}
doit();

How to order the order of returned API calls with generators?

I'm practicing some more advanced Javascript techniques, and came across generators and iterators as something I wanted to look into. I know that I'm doing this incorrectly, but I'm not really sure how to go about it.
The idea of my little program is this: I want to make API calls to the OpenWeather API for four (or more, but I'm testing with four) cities. The cities are stored in an array and one by one, the city is appended to the URL and a fetch request is sent. Each response is appended to an array and the array is sent to the client.
This was my original code:
// node/express setup here
const cities = ["London%2Cuk", "New York%2Cus", "Johannesburg%2Cza", 'Kingston%2Cjm']
const url = process.env.URL_BASE;
const headers = {
"X-RapidAPI-Host": process.env.HOST,
"X-RapidAPI-Key": process.env.API_KEY
}
const requestInit = { method: 'GET',
headers: headers
};
const fetchWeather = (ep) => {
const appendedURL = url + ep;
return fetch(appendedURL, requestInit)
.then(r => r.json());
}
app.get('/', (req, res, err) => {
const data = []
Promise.all(
cities.map( async (city) => {
await fetchWeather(city)
.then(returns => {
data.push(returns)
})
})
)
.then(() => {
res.send(data)
return data;
})
.catch(err => console.log(err))
})
Right? Solid, works ok. But now I'm stuck on how to order it. The way I would think to do this is to switch await fetchWeather(city) to yield fetchWeather(city) and have a generator manager that would continue calling next(city) until the array had completed, but I'm having an issue figuring out the pattern. I refactored the api call to a generator and am testing out a generator management function.
The paradigm I have based on my understanding is this:
First .next() starts the iteration
Second .next(args) passes the designated city to the first yield
Third .next() sends the yielded fetch request and should (ideally) return the response object that can be .then()'d.
Here is my tester generator code:
function *fetchWeather() {
for (let i = 0; i < cities.length; i++){
const appendedURL = url + (yield);
yield fetch(appendedURL, requestInit)
.then(r => {
return r.json()
});
}
}
const generatorManager = (generator) =>{
if (!generator) {
generator = fetchWeather();
}
generator.next()
generator.next(cities[i])
generator.next().value.then( e =>
console.log(e));
}
I'm getting an error:TypeError: Cannot read property 'then' of undefined And I'm not sure where I'm going wrong here with my logic. How do I refactor this to allow me to wait for specific promises if I can't individually pass known values? I know there has to be a way, but I'm missing something.
Thanks in advance.
I don't understand what benefit you hope to get from using a generator here, but the reason you're getting that error is you're doing one to many .next()'s
The first generator.next() runs fetchWeather until the first yield, which is the yield at the end of const appendedURL = url + (yield);. The return value from calling generator.next() in this case is { value: undefined, done: false }
After that, generator.next(cities[i]) resumes fetchWeather, with cities[i] being the result of the previous yield. The generator continues running, calling fetch, then calling .then on that promise, and then yielding the resulting promise. So the return value that generatorManager sees from doing generator.next(cities[i]) is { value: /* a promise object */, done: false }.
So to fix that error, you need to reduce the number of calls you're making to generator.next
generator.next()
generator.next(cities[i]).value.then(e =>
console.log(e));
As mentioned in the comments, the usual way i'd do this is map the cities to promises, and then do promise.all. For example:
Promise.all(
cities.map((city) => fetchWeather(city)) // note, this is the original fetch weather, not the generator
).then((data) => {
res.send(data);
return data;
})
.catch(err => console.log(err))

Categories

Resources