React useEffect hook infinity loop - reactjs

I keep encountering an infinity loop when trying to use the useEffect hook to fetch and set data. ive tried 3 variations of the hook and they all produce a loop, how do i stop this from happening?
useEffect(() => {
(async () => {
PostApi.getPostsByUser(auth.user._id, auth.token).then(response => setPosts(response))
})()
})
useEffect(() => {
(async () => {
PostApi.getPostsByUser(auth.user._id, auth.token).then(response => setPosts(response))
})()
},[])
useEffect(() => {
(async () => {
PostApi.getPostsByUser(auth.user._id, auth.token).then(response => setPosts(response))
})()
},[profile.posts])
EDIT: Here is the PostApi.getPostsByUser code
getPostsByUser: (userId, token) => {
return(
axios
.get("/api/posts/by/" + userId, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + token
}
})
.then(response => {
console.log("Posts by User");
console.log(response.data);
return response.data;
})
.catch(err => console.log(err))
)
}
EDIT: Function component code:
const Posts = () => {
const [{auth}] = useAuth();
const [{profile},, setPosts] = useProfile()
useEffect(() => {
PostApi.getPostsByUser(auth.user._id, auth.token)
.then(response => setPosts(response));
},[]);
console.log(profile)
return(
<div className="User-Post">
<div className="New-Post">
<NewPost />
</div>
<div className="User-Posts-Content">
{
profile.posts ? profile.posts.map((item, key) => {
return <Post post={item} key={key} />
}) : null
}
</div>
</div>
)
}
export default Posts

Change:
const [auth] = useAuth();
const [profile, setPosts] = useState();
const setPosts = posts => { setPosts(state => ({ ...state, profile: {
...state.profile, posts: posts } })) }
getPostsByUser: (userId, token) => {
return(
axios
.get("/api/posts/by/" + userId, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + token
}
});
}
and
useEffect(() => {
PostApi.getPostsByUser(auth.user._id, auth.token)
.then(response => setPosts(response.data));
},[]);

You can try like this.
useEffect(() => {
const get = async () => {
const response = await PostApi.getPostsByUser(auth.user._id, auth.token);
setPosts(response);
}
get();
},[]);

This works for me ... and the simplest solution too
const [toggle, setToggle] = useState(false);
useEffect(() => {
(async () => {
PostApi.getPostsByUser(auth.user._id, auth.token).then(response => setPosts(response))
})()
},toggle)

Related

ReactJS - passing data between components

I want to passing data between components but I have a problem. Not getting any error as long as I don't passing data yet, it's fine. When I try to show the props in the console, I can easily see what I want (history,match,location,AuthStore). But when I try to pass the data, I can only see value and AuthStore in the console and value returns empty. Where am I wrong?
front.layout.js
import Profile from '../../Views/profile'
const Layout = (props) => {
const [user, setUser] = useState({});
const [isLoggedIn, setIsLoggedIn] = useState(false);
props.AuthStore.getToken();
const history = useHistory();
useEffect(() => {
const token =
props.AuthStore.appState != null
? props.AuthStore.appState.user.access_token
: null;
axios
.post(
"/api/authenticate",
{},
{
headers: {
Authorization: "Bearer " + token,
},
}
)
.then((res) => {
if (!res.data.isLoggedIn) {
history.push("/login");
}
setUser(res.data.user);
setIsLoggedIn(res.data.isLoggedIn);
})
.catch((error) => {
history.push("/login");
});
}, []);
return (
<>
<Profile value={user} />
</>
)
index.js
const Profile = (props) => {
console.log(props);
const { params } = props.match;
const [data, setData] = useState({});
const history = useHistory();
if(props.location.key){
useEffect(() => {
axios
.get(
`/api/${params.username}`,
{
headers: {
Authorization:
"Bearer " +
props.AuthStore.appState.user.access_token,
},
}
)
.then((res) => {
if (res.data.username) {
setData(res.data);
}
})
.catch((error) => {
console.log(error);
});
}, []);
}

How to keep state update in ReactJS using Context with Hooks

I'm trying to update my user.cart which is array of objects. When I push new item in cart it's okay till I reload the page. How can I keep the state updated ?
Here is my function:
const {user, setUser} = useContext(UserContext);
const addToCart = (userId, product) => {
fetch(`${API}/cart/usercart`, {
method:"POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify([userId, product])
})
.then(() => {
const newArr = user.cart.concat(product)
setUser(oldState => ({
...oldState,
cart: newArr
}))
})
.catch(error => console.log(error))
}
Here is my UserContext:
const UserContextProvider = (props) => {
const [user, setUser] = useState({})
useEffect(() => {
fetch(`${API}/auth/user`, {
method: 'GET',
withCredentials: true,
credentials: 'include'
})
.then (response => response.json())
.then (response => {
setUser(response.user)
})
.catch (error => {
console.error (error);
});
}, [setUser])
return (
<UserProvider value={{user, setUser}}>
{props.children}
</UserProvider>
)
}
export default UserContextProvider;
You need to go through this before the question https://www.freecodecamp.org/news/state-management-with-react-hooks/

useEffect after update state

My parent component use hook useEffect for get data from API and pass props to child component.
const ParentComoponent = () => {
const [adsData, setAdsData] = useState([]);
useEffect(() => {
api
.get(`MyUrl`, { headers: authHeader() })
.then((res) => {
console.log(res);
setAdsData(res.data.data);
})
.catch((err) => {
console.log(err);
});
}, []);
return <Child adsData={adsData} />;
};
My Child component has handleDeleteClick function for delete request from API
const Child = () => {
const [deletedItem, setDeletedItem] = useState("");
const handleDeleteClick = (e, id) => {
e.preventDefault();
axios
.delete(`MyUrl`, { params: { id: id } })
.then((res) => console.log(res))
.catch((err) => console.log(err));
};
return (
<div>
// array.map Items list
<a
href=""
onClick={(e) => handleDeleteClick(e, ads.id)}
className="tables__link"
>
Delete
</a>
</div>
);
};
Delete request works successfully, but my list not updated.
How update my items list after deleted item?
You would need to pass another function that is called when a delete is executed. Something like:
const ParentComoponent = () => {
const [adsData, setAdsData] = useState([]);
const fetchData = () => {
api
.get(`MyUrl`, { headers: authHeader() })
.then((res) => {
console.log(res);
setAdsData(res.data.data);
})
.catch((err) => {
console.log(err);
});
};
const onDelete = () => {
fetchData();
};
useEffect(() => {
fetchData();
}, []);
return <Child adsData={adsData} onDelete={fetchData} />;
};
const Child = (props) => {
const [deletedItem, setDeletedItem] = useState("");
const handleDeleteClick = (e, id) => {
e.preventDefault();
axios
.delete(`MyUrl`, { params: { id: id } })
.then((res) => {
console.log(res);
props.onDelete();
})
.catch((err) => console.log(err));
};
return (
<div>
// Items list
<a
href=""
onClick={(e) => handleDeleteClick(e, ads.id)}
className="tables__link"
>
Delete
</a>
</div>
);
};
Put your delete function in the parent and pass it to the child. Then after deleting, update your list in the parent.
<ParentComponent>
const [adsData, setAdsData] = useState([]);
const handleDeleteClick = (e, id) => {
e.preventDefault();
axios
.delete(`MyUrl`, {params: {id: id}})
.then(res => {
console.log(res)
//TODO:: Implement list.pop or similar
})
.catch(err => console.log(err));
};
useEffect(() => {
api.get(`MyUrl`, { headers: authHeader() })
.then(res => {
console.log(res);
setAdsData(res.data.data);
})
.catch(err => {
console.log(err);
})
}, []);
return (
<Child
adsData={adsData}
handleClick={handleDeleteClick}
/>
)
</ParentComponent>
return (
<div>
// array.map Items list
<a href="" onClick={(e) =>
handleDeleteClick(e, ads.id)}className="tables__link">Delete</a>
</div>
)```

Trying to create a fetch request with react hooks, my data is still null

Relatively new to using hooks, but I create this useFetch hook and I am trying to sign in a user after getting their information from text fields, I am able to create a successful request by hard coding {email:someemail, password: somepassword} but the issue arises when I am trying to grab the input from my state.
The error states I am missing a parameter
Here is my component:
const Login: FunctionComponent = (props) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
// #ts-ignore
const postData =useData({email:email, password:password})
const thisWorks = useData({email:"someEmail", password:"somePassword"})
return (
<>
{console.log(thisWorks)}
<TextFieldComponent
isRequired={true}
label={'Email'}
value={email}
// #ts-ignore
handleChange={(e)=> setEmail(e.target.value) }
/>
<TextFieldComponent
isRequired={true}
label={'Password'}
value={password}
// #ts-ignore
handleChange={(e)=> setPassword(e.target.value) }
/>
<Button
onClick={() => postData}
text="Login"
/>
</>
)
}
And here is my query:
const useData = (data: loginInfo): QueryType[] | string => {
const fetched = useFetch<Query>(
"example.com",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
}
);
if (fetched.status === "loading") {
return fetched.status;
} else if (fetched.status === "loaded") {
const { payload } = fetched;
// #ts-ignore
return payload;
} else if (fetched.status === "error") {
return fetched.status;
}
return "Error";
};
my useFetch Hook:
const useFetch = <T>(url: string, headers?: Header | any) => {
const isCurrent = useRef(true);
const [result, setResult] = useState<Service<T>>({
status: "loading",
});
useEffect(
() => () => {
// called when the component is going to unmount
isCurrent.current = false;
},
[]
);
useEffect(() => {
fetch(url, headers)
.then((resp) => resp.json())
.then((response) => setResult({ status: "loaded", payload: response }))
.catch((error) => setResult({ status: "error", error }));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return result;
};
I'm curious to ask,
will you send many API requests while you typing email and password?
<Button
onClick={() => postData}
text="Login"
/>
this code doesn't seem to work, postData is not a function, just an object
may be you can modify your code like this (I'm not tested just sample code)
your component
const Login: FunctionComponent = (props) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const login = useFetch('http://path/to/login', { method: 'POST' })
React.useEffect(() => {
if (login.status === 'loaded') {
// do your stuff
}
}, [login.status])
const handleLogin = () => {
login.setPayload({ email, password })
}
if (login.status === 'loading') {
return <div>Loading...</div>
}
if (login.status === 'error') {
return <div>{login.error}</div>
}
return (
<>
<TextFieldComponent
isRequired={true}
label={'Email'}
value={email}
// #ts-ignore
handleChange={(e)=> setEmail(e.target.value) }
/>
<TextFieldComponent
isRequired={true}
label={'Password'}
value={password}
// #ts-ignore
handleChange={(e)=> setPassword(e.target.value) }
/>
<Button
onClick={handleLogin}
text="Login"
/>
</>
)
}
useFetch
const useFetch = <T>(url: string, options: RequestInit) => {
const [payload, setPayload] = React.useState({})
const [start, setStart] = React.useEffect(false)
useEffect(() => {
if (!start) {
return
}
fetch(url, {
...options,
headers: {
"Content-Type": "application/json",
...options.headers
},
body: JSON.stringify(payload),
})
.then((resp) => resp.json())
.then((response) => setResult({ status: "loaded", payload: response }))
.catch((error) => setResult({ status: "error", error }))
.finally(() => setStart(false))
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [url, start, payload, options]);
return {
...result,
setPayload: (payload) => {
setPayload(payload)
setStart(true)
}
}
}
This can be written using the useEffect hook
const useFetch = (url, options) => {
const [response, setResponse] = React.useState(null);
const [status, setStatus] = React.useState("loading");
useEffect(async () => {
try {
const res = await fetch(url, options);
const json = await res.json();
setResponse(json);
setStatus("success");
} catch (error) {
setStatus("error");
}
});
return [status, response];
};

Using react useEffect hook

I'm using React useEffect hook for getting data and display the loading indicator but my loading is not working.
Heres the useEffect Hook code:
useEffect(() => {
fetchEvents();
}, []);
fetchEvents function code:
const fetchEvents = () => {
setLoading(true);
const requestBody = {
query: `
query {
events {
_id
title
description
price
date
creator {
_id
email
}
}
}
`
};
fetch("http://localhost:5000/graphql", {
headers: {
"Content-Type": "application/json"
},
method: "POST",
body: JSON.stringify(requestBody)
})
.then(res => {
if (res.status !== 200 && res.status !== 201) {
throw new Error("Failed");
}
return res.json();
})
.then(resData => {
const events = resData.data.events;
setEvents(events);
setLoading(false);
})
.catch(err => {
console.log(err);
setLoading(false);
});
};
You should give more info but here an example for you:
import React, { useState, useEffect } from 'react';
import { Spinner } from 'react-bootstrap';
const MyComponent = () => {
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState([]);
useEffect(() => {
setIsLoading(true);
fetch('/data/endpoint')
.then((res) => res.json)
.then((response) => {
setData([...response]);
setIsLoading(false);
});
}, []);
return isLoading ? (
<Spinner />
) : (
<ol>
data.map(items => <li>{items.label}</li>);
</ol>
);
};
The first parameter of useEffect (the function) is only called if one object of the second parameter (the list) is modified. If the list is empty, it never happens.
You could remove the second parameter to apply the function fetchEvents at each update or you could use any constant to run fetchEvents only once.

Resources