react useEffect making the fetch fail - reactjs

so i have this functional compponent:
function FeedPage() {
const dispatch = useDispatch();
const globalEvents = useSelector((state: any) => state.events);
const token = localStorage.getItem("token");
useEffect(() => {
fetch("http://localhost:8080/feed/events", {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
})
.then((res) => {
return res.json();
})
.then((resData) => {
dispatch(EventsActions.fetchEvents(resData));
console.log(globalEvents);
return resData
});
}, []);
return (
<div className="EventList">
<h1>events</h1>
{globalEvents &&
globalEvents.map((event: any) => (
<div className="event-preview" key={event._id}>
<h4>{event.eventId.title}</h4>
<h4>{event.userId}</h4>
</div>
))}
</div>
);
}
and for some reson it return me empty array(when it no supposed to)
but when i do the fetch out side the useEffect hook i get the full array, but i fetch is runing in an infinate loop.
i am new to react btw

Related

Not able to implement data from one api used to get data from another

I am making a meme sharing app. In that app there are total 2 apis of getting memes.
One for memes by all the users another is only for individual user.
In second api I am able to get the data as the user id is from 3rd api.
from here i get the id of each individual.
function UserProfile({memeid}) {
const token = localStorage.getItem("token");
const [response, setResponse] = useState({});
const [id, setId] = useState('')
const userData = async() => {
await axios
.get("http://localhost:8081/userInfo/me", {
headers: { Authorization: `Bearer ${token}` },
Accept: "application/json",
"Content-Type": "application/json",
})
.then((res) => {
setResponse(res.data)
setId(res.data.id)
memeid = id
})
.catch((err)=>{
console.log(err)
})
}
console.log(id)
useEffect(()=>{
userData()
},[])
Now I want this to be used in in another api. for that is have written this code.
function MemeById({id}) {
const [response, setResponse] = useState([])
const token = localStorage.getItem("token");
// const id = "632a119672ba0e4324b18c7d"
const memes = async () => {
await axios
.get("http://localhost:8081/memes/" + id, {
headers: { Authorization: `Bearer ${token}` },
Accept: "application/json",
"Content-Type": "application/json",
})
.then((res) => {
const data = res.data;
setResponse(res.data)
console.log(data);
})
.catch((err) => {
alert(err);
console.log(err);
});
};
useEffect(()=>{
memes()
},[])
I am calling these two at User
function User() {
let id;
return (
<div>
<UserProfile memeid={id}/>
<MemeById id = {id} />
</div>
)
}
I am getting the error for this.
How to solve this error
You're making a big mistake. I think you should learn more about state and props in react.
Problem :
In your User component, you're creating a variable and passing that variable into two other component. You're trying to update the value of props from UserProfile and expecting that updated value in MemeById which is not going to work.
Solution :
function User() {
const [memeId, setMemeId] = useState(null);
return (
<div>
<UserProfile updateId={(newId) => setMemeId(newId)}/>
<MemeById memeId = {memeId} />
</div>
)
}
And in your UserProfile component
function UserProfile({updateId}) {
...
const userData = async() => {
...
// memeid = id
updateId(res.data.id)
...
}
In you MemeById component:
function MemeById({memeId}) {
...
// use memeId here
...
}

Stop react redirecting before API call has finsished

Im writing an application using react and django rest. I am trying to update a post and then redirect back to the home screen, but sometimes the redirect happens before the put request.
As there is a Get request on the home page, that then gets called first and i do not see the updated values unless i refresh the page? Any suggestions?
Here is the page with the put request (updateNote())
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { ReactComponent as ArrowLeft } from "../assets/arrow-left.svg";
const NotePage = ({ match, history }) => {
let noteId = match.params.id;
let [note, setNote] = useState(null);
useEffect(() => {
getNote();
}, [noteId]);
let getNote = async () => {
let response = await fetch(`/api/get-note/${noteId}/`);
let data = await response.json();
setNote(data);
};
let updateNote = async () => {
fetch(`/api/get-note/${noteId}/update/`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(note),
});
};
let deleteNote = async () => {
fetch(`/api/get-note/${noteId}/delete/`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
history.push("/");
};
let handleSubmit = () => {
updateNote().then(history.push("/"));
};
let handleChange = (value) => {
setNote((note) => ({ ...note, body: value }));
console.log("Handle Change:", note);
};
return (
<div className="note">
<div className="note-header">
<h3>
<ArrowLeft onClick={handleSubmit} />
</h3>
<button onClick={deleteNote}>Delete</button>
</div>
<textarea
onChange={(e) => {
handleChange(e.target.value);
}}
value={note?.body}
></textarea>
</div>
);
};
export default NotePage;
Then here is the page it redirects to
import React, { useState, useEffect } from "react";
import ListItem from "../components/ListItem";
const NotesListPage = () => {
let [notes, setNotes] = useState([]);
useEffect(() => {
getNotes();
}, []);
let getNotes = async () => {
let response = await fetch("/api/get-notes/");
let data = await response.json();
setNotes(data);
};
return (
<div className="notes">
<div className="notes-header">
<h2 className="notes-title">☶ Notes</h2>
<p className="notes-count">{notes.length}</p>
</div>
<div className="notes-list">
{notes.map((note, index) => (
<ListItem key={index} note={note} />
))}
</div>
</div>
);
};
export default NotesListPage;
I want to make sure that history.push("/") doesnt get executed unitll the fetch request has returned a response
I suggest using the promise method and using '.then' or await just like that :
let updateNote = async () => {
let temp =await fetch(`/api/get-note/${noteId}/update/`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(note),
});
if(temp)
history.push("/")
};
If you want to navigate after the fetch request has resolved then the code needs to wait for them to settle. Don't forget to catch and/or handle any errors and rejected Promises appropriately.
Example:
const updateNote = async () => {
// return Promise to chain from
return fetch(`/api/get-note/${noteId}/update/`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(note),
});
};
const deleteNote = async () => {
try {
// wait for Promise to resolve
await fetch(`/api/get-note/${noteId}/delete/`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
history.push("/");
} catch(error) {
// log error, etc...
}
};
const handleSubmit = () => {
// pass a callback in .then
updateNote()
.then(() => history.push("/"))
.catch(error => {
// log error, etc...
});
};

React - how to wait for API response to return component?

I am new to react and I am building a page component that returns data from an API call and passes the values to my return statement. My page continues to return as blank because the page loads before the variables are returned from the API. I am wondering, how can I wait to render the page until my API has returned a response? The two variables are initialized as so and are not updated until the API response
var userData
const [customer_email, setEmail] = useState();
const [newuserid, setUserId] = useState();
useEffect(() => {
userData = Cookies.get("user-data");
if (userData) {
console.log("Userdata !== null");
try {
userData = JSON.parse(userData);
} catch (e) {
console.log(e);
}
setEmail(userData.email);
setUserId(userData.userID);
}
}, []);
function getCustomer() {
const options = {
method: "GET",
headers: {
Accept: "application/json",
"x-guid": "......",
"x-api-key": ".....",
},
};
if (customer_email != "" && customer_email != undefined) {
try {
console.log("email inside fetch =", customer_email);
fetch(
`https://exampleapi/customers?customer_email=${customer_email}&customer_id=${newuserid}`,
options
)
.then((response) => response.json())
.then((response) => console.log(response))
.catch((err) => console.log(err));
} catch (e) {
console.log(e);
}
}
}
if (customer_email) {
console.log("get customer");
getCustomer();
}
The component return statement:
return (
<>
{customer_email && (
<section>
<div
id="identification"
data-authenticated="true"
data-email={customer_email}
data-id={newuserid}
style={{ display: "none" }}
></div>
</section>
<div>
........{irrelevant html here}
</div>
)}
);
Note---- This is not a class, it is a function component
You might want to do something like this where you have a loading state that you set to true when the response from the API has been resolved, which will re-render the component.
Note: This code will not work if you copy and paste. It's just a representation of the pattern you should use.
var userData
const [customer_email, setEmail] = useState();
const [newuserid, setUserId] = useState();
cost [hasLoaded, setHasLoaded] = useState(false);
function getCustomer() {
const options = {
method: "GET",
headers: {
Accept: "application/json",
"x-guid": "......",
"x-api-key": ".....",
},
};
if (customer_email != "" && customer_email != undefined) {
try {
console.log("email inside fetch =", customer_email);
fetch(
`https://exampleapi/customers?customer_email=${customer_email}&customer_id=${newuserid}`,
options
)
.then((response) => response.json())
.then((response) => console.log(response))
.catch((err) => console.log(err));
} catch (e) {
console.log(e);
}
}
}
useEffect(() => {
userData = Cookies.get("user-data");
if (userData) {
console.log("Userdata !== null");
try {
userData = JSON.parse(userData);
} catch (e) {
console.log(e);
}
setEmail(userData.email);
setUserId(userData.userID);
}
}, []);
useEffect(async () => {
if (!customer_email) return;
console.log("getting customer")
const customerData await getCustomer()
if (customerData) setHasLoaded(true)
}, [])
return(
<>
{hasLoaded && <div>.....</div>}
</>
)

useEffect: How to put data in the state in order

I'd like to ask how to retrieve data through use Effect.
The flow I want is as follows.
First, I want to get the 'cards' state, fill the cards with data, and then fill the data through the cardsPromises after that.
But my code couldn't get cards and wordAll, and the empty value came out.
I think it's because the cards are still empty, but I don't know how to operate in order.
Please tell me how to do it.
const [wordAll, setWordAll] = useState([]);
const [cards, setCards] = useState([]);
useEffect(() => {
axios
.get("http/api/words/", {
headers: {
Authorization: cookies.token,
},
})
.then((response) => {
setCards(response.data);
})
.catch((error) => {
console.log(error);
});
const cardsPromises = cards.map((contents) =>
axios.get(
`http/api/words/detail_list/?contents=${contents.contents}`,
{
headers: {
Authorization: cookies.token,
},
}
)
);
console.log("cards", cards);
Promise.all(cardsPromises)
.then((response) => {
console.log("resp", response.data);
setWordAll(response.data);
})
.catch((error) => {
console.log("err==>", error);
});
}, []);
You are correct, cards array is still empty in the useEffect callback when the fetching the data. I suggest converting to async/await and waiting for the first fetch to resolve and using that value of cards for the fetching of the rest of the data.
const [wordAll, setWordAll] = useState([]);
const [cards, setCards] = useState([]);
useEffect(() => {
const fetchData = async () => {
try {
const{ data: cards } = await axios.get(
"http/api/words/",
{
headers: {
Authorization: cookies.token,
},
},
);
setCards(cards);
const cardsPromises = cards.map((contents) =>
axios.get(
`http/api/words/detail_list/?contents=${contents.contents}`,
{
headers: {
Authorization: cookies.token,
},
}
);
);
const wordAllResponse = await Promise.all(cardsPromises);
const wordAll = wordAllResponse.map(({ data }) => data);
setWordAll(wordAll);
} catch (error) {
// handle any errors, rejected Promises, etc..
}
};
fetchData();
}, []);
Wrap your 2nd axios call inside a function, and call it after 1st axios call returns.
useEffect(() => {
const getWords = (cards) => {
const cardsPromises = cards.map((contents) =>
axios.get(
`http/api/words/detail_list/?contents=${contents.contents}`,
{
headers: {Authorization: cookies.token}
}
)
);
Promise.all(cardsPromises)
.then((response) => {
setWordAll(response.data);
})
.catch((error) => {
console.log("err==>", error);
});
})
axios
.get("http/api/words/", {
headers: { Authorization: cookies.token },
})
.then((response) => {
const cards = response.data;
setCards(cards);
getWords(cards);
})
.catch((error) => {
console.log(error);
});
}, [])
Now dependency chain is clearer.

ReactJS how can I fetch data only once a state has been updated, and only once [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 9 months ago.
I am new to React and am having a difficult time figuring out how I can wait for the state to have a specific (not null) update before fetching data. I am using firebase JWT and am passing the token into the headers but with my current code it runs and passed the value of null. Is there a nifty hook trick to ensure that my fetchData function only runs once and that it only runs after the token value is set?
I tried setting the state as const [token, setToken] = useState(auth.currentUser.getIdToken()); but it appears to return a promise into the header and not the token (guessing its because its async). Thanks!
import React, { useState, useEffect } from 'react';
import { auth } from '../../firebase-config';
const RecordEntry = (props) => {
const [token, setToken] = useState();
const [isLoading, setIsLoading] = useState(false);
var mydata =
{
entry_id = props.entry_id
}
//should only call this once
const fetchData = async () => {
const current_token = auth.currentUser.getIdToken();
setToken(current_token);
//need to yield here to verify token is set and not null - this is where I am stuck
fetch('https://mysite/api/recordEntry' , {
method: 'POST',
headers: new Headers({
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
}),
body: JSON.stringify(mydata)
})
.then((response) => response.json())
.then((data) => {
setIsLoading(false);
})
.catch((error) => {
setIsLoading(false);
console.log(error);
});
};
//passing empty array so the effect only runs once
useEffect(() => {
fetchData();
}, []);
if (isLoading) {
return <div>Loading...</div>;
}
return (
<div>
<h1> Entry Recorded </h1>
</div>
);
};
export default RecordEntry;
Try this solution
const [didFetch,setDidFetch] = useState(false)
useEffect(() => {
if(!didFetch){
setDidFetch(true)
fetchData();
}
}, []);
"Thanks for the response, I attempted this solution but the token is still not updated. The header shows it's a promise object, instead of the expected token string. The token is still awaiting to update essentially. I need a method that pauses the data fetch until the token is filled."
So try this:
const [token, setToken] = useState(null);
And
useEffect(() => {
if (token != null) fetchData();
}, [token]);
Using #awolf's suggestion of await for current_token and then bass that as the auth bearer instead of the version updating to state. Worked perrfectly. Here is the final solution:
import React, { useState, useEffect } from 'react';
import { auth } from '../../firebase-config';
const RecordEntry = (props) => {
const [token, setToken] = useState();
const [isLoading, setIsLoading] = useState(false);
var mydata =
{
entry_id = props.entry_id
}
//should only call this once
const fetchData = async () => {
const current_token = await auth.currentUser.getIdToken();
setToken(current_token);
//need to yield here to verify token is set and not null - this is where I am stuck
fetch('https://mysite/api/recordEntry' , {
method: 'POST',
headers: new Headers({
"Content-Type": "application/json",
Authorization: `Bearer ${current_token}`,
}),
body: JSON.stringify(mydata)
})
.then((response) => response.json())
.then((data) => {
setIsLoading(false);
})
.catch((error) => {
setIsLoading(false);
console.log(error);
});
};
//passing empty array so the effect only runs once
useEffect(() => {
fetchData();
}, []);
if (isLoading) {
return <div>Loading...</div>;
}
return (
<div>
<h1> Entry Recorded </h1>
</div>
);
};
export default RecordEntry;

Resources