import { FETCH_DATA } from "./types";
export const fetchData = () => dispatch => {
const array = [];
fetch(
"https://example-api-endpoint.com"
)
.then(res => res.json())
.then(data =>
data.forEach(element => {
fetch(
"https://another-example-api-endpoint.com"
)
.then(res => res.json())
.then(data => {
array.push(data);
dispatch({
type: FETCH_DATA,
payload: array
});
});
})
);
};
Currently, I am dispatching for every element. I was wondering if there was a way I could dispatch after every iteration of the forEach has run.
It's a bit primitive but here we go:
import { FETCH_DATA } from "./types";
export const fetchData = () => dispatch => {
const array = [];
var dispatchData = () => {
dispatch({
type: FETCH_DATA,
payload: array
});
}
fetch(
"https://example-api-endpoint.com"
)
.then(res => res.json())
.then(data =>{
var fetchCount = 0
data.forEach((element,index) => {
fetch(
"https://another-example-api-endpoint.com"
)
.then(res => res.json())
.then(data => {
array.push(data);
fetchCount++;
if(fetchCount === data.length){
dispatchData()
}
});
})
});
};
You could map the final promises into an array and then dispatch in Promise.all.
import { FETCH_DATA } from "./types";
export const fetchData = () => dispatch => {
fetch("https://example-api-endpoint.com")
.then(res => res.json())
.then(data => {
const promises = data.map(element =>
fetch("https://another-example-api-endpoint.com").then(res =>
res.json()
)
);
Promise.all(promises).then(payload =>
dispatch({
type: FETCH_DATA,
payload
})
);
});
};
Related
I am building a Project with the Pokemon API. Here it is how I am fetching the data:
pokeAPI.js
export const api = {
getPokemonList: async ({ url }) => {
return new Promise((resolve) => {
fetch(url)
.then(res => res.json())
.then(data => {
resolve(data)
})
});
},
getPokemonInfo: async (url) => {
return new Promise((resolve) => {
fetch(url)
.then(res => res.json())
.then(data => {
resolve(data)
})
});
}
};
App.js
const [pokemon, setPokemon] = useState([]);
const URL = 'https://pokeapi.co/api/v2/pokemon?limit=150';
useEffect(() => {
const getPokemonInfo = async () => {
const json = await api.getPokemonInfo(URL);
await loadPokemon(json.results);
};
getPokemonInfo();
}, []);
const loadPokemon = async (data) => {
let pokemonData = await Promise.all(data.map(async pokemon => {
let pokemonList = await api.getPokemonList(pokemon)
return pokemonList
}))
setPokemon(pokemonData);
};
Although this works, it's currently calling getPokemonList for every pokemon and the fact that there are multiple async / await is not helping with readiability. How could I refactor this logic:
const loadPokemon = async (data) => {
let pokemonData = await Promise.all(data.map(async pokemon => {
let pokemonList = await api.getPokemonList(pokemon)
return pokemonList
}))
setPokemon(pokemonData);
};
to fetch only once, using a memoized value in a hook to prevent multiple re-renders?
Thanks for helping.
`there are several ways to do it, you can use redux like state managements or browsers local storage
const App = () => {
const [pokemons, setPokemons] = useState([]);
useEffect(() => {
let pokemons= localStorage.getItem("users");
if (pokemons) {
pokemons = JSON.parse(pokemons);
setPokemons({ pokemons });
} else {
fetch("https://pokeapi.co/api/v2/pokemon?limit=150")
.then(res => res.json())
.then(pokemons => {
setPokemons({ pokemons });
localStorage.setItem("pokemons", JSON.stringify(pokemons));
});
}
}, [])
}
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/
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)
I need some help with my code where it has to display the API data when the condition is the following
use componentWillReceiveProps()
when count == 2 it will fetch api
import React, {useState, useEffect,Component} from "react";
var x = 2;
const App =() => {
const [hasError, setErrors]= useState(false)
const [apps, setApps] = useState ("Loading");
useEffect(()=>{
async function fetchData(){
const res = await fetch ("https://m2dzass19b.execute-api.ap-southeast-1.amazonaws.com/dev/api/vdrOptimize/21-09-2019");
res
.json()
.then(res => setApps(res))
.catch(err => setErrors(err))
}
fetchData();
} );
return(
<div>
<span>{JSON.stringify(apps)}</span>
<hr/>
</div>
);
}
Okay here's the solution
Change your useEffect as this
useEffect(() => {
async function fetchData() {
const res = await fetch(
"https://m2dzass19b.execute-api.ap-southeast-1.amazonaws.com/dev/api/vdrOptimize/21-09-2019"
);
res
.json()
.then(res => setApps(res))
.catch(err => setErrors(err));
}
if (props.id === 2) {
fetchData();
}
}, [props.id]);
Hope it helps
UPDATE: This is how you can do it using comonentWillReceiveProps
UNSAFE_componentWillReceiveProps(nextProps) {
async function fetchData() {
const res = await fetch(
"https://m2dzass19b.execute-api.ap-southeast-1.amazonaws.com/dev/api/vdrOptimize/21-09-2019"
);
res
.json()
.then(res => this.setState({ apps: res }))
.catch(err => this.setState({ hasError: err }));
}
if (nextProps.id === 2) {
fetchData.call(this);
}
}
Sandbox
Note: componentWillReceiveProps() is deprecated lifecycle method
Trying to use props from <button> of component in the dispatch of a redux-thunk function that has been set up for Async process but I can't quite get how to use both props and the function (that's being connected to the component through react-redux connect in the mapDispatchToProps) but I just can't figure out how to call both the props and the function.
function loadData(dispatch, medium) {
console.log(dispatch)
return dispatch({type: 'LOADING_COMPONENT'})
return axios.get(`/professionals/${medium}`)
.then(res => res.json())
.then(
data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
);
}
const mapDispatchToProps = (dispatch) => {
return {
LogInClick : () => dispatch(loadData()),
}
}
const LoginButtons = ({props, LogInClick}) => (
<button onClick={() => LogInClick(props.medium)} type="button">{props.buttonName}</button>
)
const LoginConnect = connect(null, mapDispatchToProps)(LoginButtons)
And Then I export that and try to call it so it can be reused in the render file like
<LoginConnect medium='suhhhh' buttonName='To log in to suhhh'/>
function loadData(dispatch, medium) {
console.log(dispatch)
return dispatch({type: 'LOADING_COMPONENT'})
return axios.get(`/professionals/${medium}`)
.then(res => res.json())
.then(
data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
);
}
const mapDispatchToProps = (dispatch) => {
return {
LogInClick : () => dispatch(loadData()),
}
}
const LoginButtons = ({medium, buttonName, LogInClick}) => (
<button onClick={() => LogInClick(medium)} type="button">{buttonName}</button>
)
const LoginConnect = connect(null, mapDispatchToProps)(LoginButtons)
This should work !! actually connect merges mapStateToProps, mapDispatchToProps into this.props. Read this documenation for better understanding https://github.com/reactjs/react-redux/blob/master/docs/api.md
Try returning a function, which redux-thunk will then call with dispatch as an argument.
You can then call dispatch from that returned function:
function loadData(medium) {
return (dispatch) => {
dispatch({ type: 'LOADING_COMPONENT' })
axios.get(`/professionals/${medium}`)
.then(res => res.json())
.then(
data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
)
}
}
Your LogInClick function can then take an argument which can be passed into loadData:
const mapDispatchToProps = (dispatch) => {
return {
LogInClick: (medium) => dispatch(loadData(medium)),
}
}
const LoginButtons = (props) => (
<button onClick={() => props.LogInClick(props.medium)} type="button">{props.buttonName}</button>
)
I hope this helps.