I want to delete all completed tasks in my ToDo list.
In the code, I assign all completed tasks to the variable "completed". Next, I want to delete completed tasks from the server using axios.
removeItems = (event) => {
event.preventDefault();
let completed = [];
this.setState(prevState => {
return {
todos: prevState.todos.filter(todo => {
if (todo.completed == true) {
completed.push(todo)
}
return !todo.completed;
})
}
})
console.log(completed);
const remove = completed.map(async (todo) => {
await Axios.delete(`http://localhost:8000/todoes_destroy/{id}/`)
})
}
I changed the code and now it works. This is the solution,
removeItems = async (event) => {
event.preventDefault();
const completed = this.state.todos.filter(todo => {
return todo.completed
});
const remove = completed.map( async (todo) => {
console.log(todo);
await Axios.delete(`http://localhost:8000/todoes_destroy/${todo.id}/`)
}
)
const {data} = await Axios.get('http://127.0.0.1:8000/todoes_read/', {
})
this.setState({todos : data})
}
Related
How do I update existing records by their index key?
Im not so familiar with React Query.
When a button is clicked, then this will trigger onClickHandler to update the object value by its index key.
import {useQuery, useQueryClient} from '#tanstack/react-query';
const {
data: comments,
isError,
isLoading
} = useQuery({
queryKey: ['comments'],
queryFn: async () => {
const response = await fetch(`https://jsonplaceholder.typicode.com/posts/1/comments`);
return response.json();
}
});
const onClickHandler = (index) => {
const previousData = queryClient.getQueriesData(['comments']);
queryClient.setQueryData(['comments'], (comments) => {
comments.map((r, i) => {
r['is_shown'] = false;
if(i === index) {
r['is_shown'] = true;
}
return r;
});
});
};
you forgot to return the new data inside setQueryData function.
const Board = () => {
...
const {selected} = useSelector(state => state.board);
// In Redux reducer ->
// const initialState = {
// selected : {}
// }
const getOnePost = async (id) => {
try {
const response = await axios.get(`/api/v1/post/${id}`);
const getOnePostData = await response.data;
selected = getOnePostData //I want to use Redux to use global state...
console.log(selected) //TypeError... and How to use global state..?
} catch(error) {
alert(error.response.data.message);
return Promise.reject(error)
}
}
const postClickHandler = (postId) =>
{
if(postId) {
// dispatch(boardSelected(postId));
getOnePost(postId)
}
}
...
}
This code uses axios.get to receive post information.
and I want to use api's return to global state(Redux state).
const getOnePost = async (id) => {
try {
const response = await axios.get(`/api/v1/post/${id}`);
const getOnePostData = await response.data;
return getOnePostData //return data
} catch(error) {
alert(error.response.data.message);
return Promise.reject(error)
}
}
const postClickHandler = (postId) =>
{
if(postId) {
getOnePost(postId).then((response)=>{
return{...selected, selected: response} //But Seleted: {}
})
}
}
Axios is a Promised-based JavaScript library that is used to send HTTP requests,
you have to get the promise result in the then method .
try this
const Board = () => {
...
const {selected} = useSelector(state => state.board);
// In Redux reducer ->
// const initialState = {
// selected : {}
// }
const getOnePost = async (id) => {
try {
const response = await axios.get(`/api/v1/post/${id}`);
response.then(function (rec) {
selected = rec.data //
//here update you state
console.log(selected)
}).catch(function (error) {
console.log(error);
});
} catch(error) {
alert(error.response.data.message);
return Promise.reject(error)
}
}
const postClickHandler = (postId) =>
{
if(postId) {
// dispatch(boardSelected(postId));
getOnePost(postId)
}
}
...
}
You can handle promise by using .then()
Try to initialize selected like below :
const response = await axios.get(`/api/v1/post/${id}`).then( response => {
selected = response
})
I don't know what kind of data is it. So, try to log response data, and use it.
I have a section on a webpage for a task :
In the form i write an email which is 'validated' later with a function.First when i submit an email which passed the validation it sends a sendSubscribe function to the server,after that i click the button unsubscribe and it sends a unsubscribeUser function.But,after that,when i click on the email input,it starts to send unsubscribe fetch requests everytime and when i click on the subscribe button it also does the same.
The network tab looks like this:
I think i know which is the problem,but i dont know how to fix it.My idea is that everytime i click on the subscribe button it ataches an event listener from the function,thats why it fires multiple unsubscribe requests.
Subscribe functions : the subscribeEmail is the most important
import { validateEmail } from './email-validator.js'
import { unsubscribeUser } from './unsubscribeFetch.js'
export const subscribe = () => {
const subscribeBtn = document.getElementById('subscribeButton')
subscribeBtn.setAttribute('value', 'Unsubscribe')
document.getElementById('emailForm').style.display = 'none'
localStorage.setItem('isSubscribed', 'true')
document.getElementById('submit-info').value = ''
}
export const unsubscribe = () => {
const subscribeBtn = document.getElementById('subscribeButton')
subscribeBtn.setAttribute('value', 'Subscribe')
document.getElementById('emailForm').style.display = 'block'
localStorage.setItem('isSubscribed', 'false')
}
export const subscribeEmail = (email) => {
const isValidEmail = validateEmail(email)
if (isValidEmail === true) {
subscribe()
document.querySelector('form').addEventListener('click', function (e) {
unsubscribe()
unsubscribeUser()
localStorage.removeItem('Email')
e.stopPropagation()
})
} else if (isValidEmail === false) {
unsubscribe()
}
}
Subscribe fetch functions:
import { validateEmail } from './email-validator.js'
export const sendSubscribe = (emailInput) => {
const isValidEmail = validateEmail(emailInput)
if (isValidEmail === true) {
sendData(emailInput)
}
}
export const sendHttpRequest = (method, url, data) => {
return fetch(url, {
method: method,
body: JSON.stringify(data),
headers: data
? {
'Content-Type': 'application/json'
}
: {}
}).then(response => {
if (response.status >= 400) {
return response.json().then(errResData => {
const error = new Error('Something went wrong!')
error.data = errResData
throw error
})
}
return response.json()
})
}
const sendData = (emailInput) => {
sendHttpRequest('POST', 'http://localhost:8080/subscribe', {
email: emailInput
}).then(responseData => {
return responseData
}).catch(err => {
console.log(err, err.data)
window.alert(err.data.error)
})
}
Unsubscribe fetch function:
export const unsubscribeUser = () => {
fetch('http://localhost:8080/unsubscribe', { method: 'POST' }).then(response => { console.log(response.status) })
}
Subscribe button event listener:
document.querySelector('form').addEventListener('submit', async function (e) {
// create a variable to store localStorage email value
const introducedEmail = inputForm.value
e.preventDefault()
console.log(introducedEmail)
localStorage.setItem('Email', introducedEmail)
subscribeEmail(introducedEmail) //change the button style and set in local storage isSubscribed to true
sendSubscribe(introducedEmail) //send subscribe fetch to the server
// prevent additional requests upon clicking on "Subscribe" and "Unsubscribe".
if (isFetching) return // do nothing if request already made
isFetching = true
disableBtn()
const response = await fetchMock() //eslint-disable-line
isFetching = false
enableBtn()
})
// functions for disabling the submit button when a fetch request is in progress
const fetchMock = () => {
return new Promise(resolve => setTimeout(() => resolve('hello'), 2000))
}
const disableBtn = () => {
submitForm.setAttribute('disabled', 'disabled')
submitForm.style.opacity = '0.5'
}
const enableBtn = () => {
submitForm.removeAttribute('disabled')
submitForm.style.opacity = '1'
}
}
Could you guys please help me? I have no idea how to fix this.Thanks in advance!
I modified all your functions and fixed your problem, implemented async and await.
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await
Subscribe fetch functions
import { validateEmail } from './email-validator.js'
export const sendSubscribe = async (emailInput) => {
const isValidEmail = validateEmail(emailInput) // idk if this is async func
if (isValidEmail === true) {
await sendData(emailInput);
}
}
export const sendHttpRequest = async (method, url, data) => {
return await fetch(url, {
method: method,
body: JSON.stringify(data),
headers: data
? {
'Content-Type': 'application/json'
}
: {}
}).then(response => {
if (response.status >= 400) {
return response.json().then(errResData => {
const error = new Error('Something went wrong!')
error.data = errResData
throw error
})
}
return response.json()
})
}
const sendData = async (emailInput) => {
await sendHttpRequest('POST', 'http://localhost:8080/subscribe', {
email: emailInput
}).then(responseData => {
return responseData
}).catch(err => {
console.log(err, err.data)
window.alert(err.data.error)
})
}
Unsubscribe fetch function
export const unsubscribeUser = async () => {
await fetch('http://localhost:8080/unsubscribe', { method: 'POST' }).then(response => { console.log(response.status) })
}
Subscribe button event listener
let isFetching = false;
document.querySelector('form').addEventListener('submit', async function (e) {
e.preventDefault();
if (isFetching) return // do nothing if request already made
// create a variable to store localStorage email value
const introducedEmail = inputForm.value;
console.log(introducedEmail);
localStorage.setItem('Email', introducedEmail);
// prevent additional requests upon clicking on "Subscribe" and "Unsubscribe".
disableBtn();
await fetchMock();
isFetching = true;
await subscribeEmail(introducedEmail); //change the button style and set in local storage isSubscribed to true
await sendSubscribe(introducedEmail); //send subscribe fetch to the server
// data sent, reenabling button
isFetching = true
enableBtn();
});
// functions for disabling the submit button when a fetch request is in progress
...
const fetchMock = () => {
return new Promise(resolve => setTimeout(() => resolve('hello'), 2000))
}
const disableBtn = () => {
submitForm.setAttribute('disabled', 'disabled')
submitForm.style.opacity = '0.5'
}
const enableBtn = () => {
submitForm.removeAttribute('disabled')
submitForm.style.opacity = '1'
}
}
So,like i said,the problem was in the function that attached a new event listener to the button,thats why unsubscribe request was sent everytime with +1 more request. So,i did this way :
Function for the submit button:
let isUsed = false
const submitClickButton = async () => {
// create a variable to store localStorage email value
const introducedEmail = inputForm.value
//e.preventDefault()
console.log(introducedEmail)
localStorage.setItem('Email', introducedEmail)
subscribeEmail(introducedEmail) //change the button style and set in local storage isSubscribed to true
sendSubscribe(introducedEmail) //send subscribe fetch to the server
// prevent additional requests upon clicking on "Subscribe" and "Unsubscribe".
if (isFetching) return // do nothing if request already made
isFetching = true
disableBtn()
const response = await fetchMock() //eslint-disable-line
isFetching = false
enableBtn()
isUsed = true
}
const undoClickButton = () => {
//e.preventDefault()
//unsubscribeEmail()
unsubscribeEmail()
isUsed = false
}
const toggleButton = () => {
isUsed ? undoClickButton() : submitClickButton()
}
submitForm.addEventListener('click', toggleButton, false)
And subscribeEmail function :
export const subscribeEmail = (email) => {
const isValidEmail = validateEmail(email)
if (isValidEmail === true) {
subscribe()
// document.querySelector('form').addEventListener('click', function (e) {
// unsubscribe()
// unsubscribeUser()
// localStorage.removeItem('Email')
// e.stopPropagation()
// })
} else if (isValidEmail === false) {
unsubscribe()
}
}
export const unsubscribeEmail = () => {
// const isValidEmail = validateEmail(email)
// if (isValidEmail===true){
unsubscribe()
unsubscribeUser()
localStorage.removeItem('Email')
//}
}
I'am doing the StarWars API task for the job interview.
my code look like that and I don't know in what place the setCharacters hook should be.
First the page is rendered and then the state is set.
I need the page to be rendered when all the fetches are done.
To try to be more efficient i changed the previous fetches into Promise.all() but right now I'am stuck with the setCharacters placement.
The previous topic can be seen here useEffect efficiency in Star Wars API
const api = `https://swapi.dev/api/people/`;
const [characters, setCharacters] = useState([]);
const [fetched, setFetched] = useState(false);
useEffect(() => {
const fetchOtherData = (characters) => {
const charactersWithAllData = [];
characters.forEach((character) => {
const homeworld = character.homeworld;
const species = character.species;
const vehicles = character.vehicles;
const starships = character.starships;
let urls = [homeworld, ...species, ...vehicles, ...starships];
Promise.all(
urls.map((url) => {
if (url.length) {
fetch(url)
.then((response) => response.json())
.then((data) => {
if (url.search("species") > 0) {
character.species = data.name;
}
if (url.search("planets") > 0) {
character.homeworld = data.name;
}
if (url.search("vehicles") > 0) {
character.vehicles.shift();
character.vehicles.push(data.name);
}
if (url.search("starships") > 0) {
character.starships.shift();
character.starships.push(data.name);
}
})
.catch((err) => console.error(err));
}
if (!url.length) {
if (url.search("species")) {
character.species = "Unspecified";
}
if (url.search("vehicles")) {
character.vehicles = "";
}
if (url.search("starships")) {
character.starships = "";
}
}
})
).then(charactersWithAllData.push(character));
});
setCharacters(charactersWithAllData);
};
const fetchApi = () => {
const characters = [];
Promise.all(
[api].map((api) =>
fetch(api)
.then((response) => response.json())
.then((data) => characters.push(...data.results))
.then((data) => fetchOtherData(characters))
.then(setFetched(true))
)
);
};
fetchApi();
}, []);
Thanks for all the of the possible replies.
You can add a loading state, which by default is set to true. After you finish fetching the data from the api you can set loading to false and then render the page.
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchOrdersData = Promise.all().then((data) => {
//Fetch all data
.....
//Render page after loading is false
setLoading(false);
}).catch()
}, [])
Issue
Found a few issues:
Primary Issue: The fetchApi function's Promise.all chain was immediately invoking setFetched(true) instead of waiting for the chain's thenable to invoke it. This is why you are seeing the empty table before the data has been fetched and processed.
const fetchApi = () => {
const characters = [];
Promise.all(
[api].map((api) =>
fetch(api)
.then((response) => response.json())
.then((data) => characters.push(...data.results))
.then((data) => fetchOtherData(characters))
.then(setFetched(true)) // <-- immediately invoked
)
);
};
fetchOtherData function is also synchronous and doesn't return a Promise so the outer Promise-chain in fetchApi doesn't wait for the processing to complete, this is why you still see URL strings instead of the resolved values from the fetch.
Solution
Fix the Promise chain in fetchApi to be invoked in the .then callback. Rework the logic in fetchOtherData to wait for the additional fetch requests to resolve.
Here's my suggested solution:
const api = `https://swapi.dev/api/people/`;
function App() {
const basicClassName = "starWarsApi";
const [characters, setCharacters] = useState([]);
const [fetched, setFetched] = useState(false);
useEffect(() => {
const fetchOtherData = async (characters) => {
const characterReqs = characters.map(async (character) => {
const { homeworld, species, starships, vehicles } = character;
const urls = [homeworld, ...species, ...vehicles, ...starships];
// Make shallow copy to mutate
const characterWithAllData = { ...character };
const urlReqs = urls.map(async (url) => {
if (url.length) {
try {
const response = await fetch(url);
const { name } = await response.json();
if (url.includes("species")) {
characterWithAllData.species = name;
}
if (url.includes("planets")) {
characterWithAllData.homeworld = name;
}
if (url.includes("vehicles")) {
characterWithAllData.vehicles.shift();
characterWithAllData.vehicles.push(name);
}
if (url.includes("starships")) {
characterWithAllData.starships.shift();
characterWithAllData.starships.push(name);
}
} catch (err) {
console.error(err);
}
} else {
if (url.includes("species")) {
characterWithAllData.species = "Unspecified";
}
if (url.includes("vehicles")) {
characterWithAllData.vehicles = "";
}
if (url.includes("starships")) {
characterWithAllData.starships = "";
}
}
});
await Promise.all(urlReqs); // <-- wait for mutations/updates
return characterWithAllData;
});
return Promise.all(characterReqs);
};
const fetchApi = () => {
Promise.all(
[api].map((api) =>
fetch(api)
.then((response) => response.json())
.then((data) => fetchOtherData(data.results))
.then((charactersWithAllData) => {
setCharacters(charactersWithAllData);
setFetched(true);
})
)
);
};
fetchApi();
}, []);
return (
<div className={basicClassName}>
{fetched && (
<>
<h1 className={`${basicClassName}__heading`}>Characters</h1>
<div className={`${basicClassName}__inputsAndBtnsSection`}>
<FilteringSection
basicClassName={`${basicClassName}__inputsAndBtnsSection`}
/>
<ButtonsSection
basicClassName={`${basicClassName}__inputsAndBtnsSection`}
/>
</div>
<CharactersTable characters={characters} />
</>
)}
</div>
);
}
I am using a recursive function to make async calls if there is an odata nextlink. It works fine as it is by using map to push the items into teamsArray. The problem hover is that I am looping through each item instead of merging the objects together. I tried to use the following but with no avail:
teamsArray = {}
teamsArray = { ...teamsArray, ...latstestResults}
Current code that does work but is not optimized:
export const fetchAllTeams = () => {
return dispatch => {
dispatch(fetchAllTeamsRequest());
};
};
export const fetchAllTeamsRequest = () => {
return dispatch => {
dispatch(getAllTeamStarted());
let teamsArray = [];
getAllTeams("", teamsArray, dispatch);
};
};
const getAllTeams = (url, teamsArray, dispatch) => {
if (url === "") {
url = "https://graph.microsoft.com/v1.0/me/memberOf?$top=10";
}
const getTeams = adalGraphFetch(fetch, url, {})
.then(response => {
if (response.status != 200 && response.status != 204) {
dispatch(fetchAllTeamsFailure("fout"));
return;
}
response.json().then(result => {
if (result["#odata.nextLink"]) {
const teams = objectToArray(result.value);
teams.map(team => {
teamsArray.push(team);
});
getAllTeams(result["#odata.nextLink"], teamsArray, dispatch);
} else {
const latestResult = objectToArray(result.value);
latestResult.map(team => {
teamsArray.push(team);
});
console.log("the teams", teamsArray);
dispatch(fetchAllTeamsSucces(result));
}
});
})
.catch(error => {
dispatch(fetchAllTeamsFailure(error));
});
};
Something like this might work for you.
I refactored the paged fetching into an async function that calls itself if there are more items to fetch, then eventually resolves with the full array of results.
Dry-coded, so there may be bugs and YMMV, but hope it helps.
export const fetchAllTeams = () => {
return dispatch => {
dispatch(fetchAllTeamsRequest());
};
};
export const fetchAllTeamsRequest = () => {
return async dispatch => {
dispatch(getAllTeamStarted());
try {
const teamsArray = await getPaged(
"https://graph.microsoft.com/v1.0/me/memberOf?$top=10",
);
dispatch(fetchAllTeamsSucces(teamsArray));
} catch (err) {
dispatch(fetchAllTeamsFailure(err));
}
};
};
const getPaged = async (url, resultArray = []) => {
const response = await adalGraphFetch(fetch, url, {});
if (response.status != 200 && response.status != 204) {
throw new Error("failed to fetch teams");
}
const result = await response.json();
objectToArray(result.value).forEach(team => resultArray.push(team));
if (result["#odata.nextLink"]) {
// Get more items...
return getPaged(resultArray, result["#odata.nextLink"]);
}
return resultArray; // All done, return the teams array.
};