import React, { useEffect, useState } from "react";
import { endpoint, apiKey } from "../api";
import Container from "../components/layouts/Container";
export default function Movie({ route }) {
const { movieId } = route.params;
const [movieDetails, setMovieDetails] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const urls = [
`${endpoint}/movie/${movieId}?api_key=${apiKey}`,
`${endpoint}/movie/${movieId}/credits?api_key=${apiKey}`,
`${endpoint}/movie/${movieId}/images?api_key=${apiKey}`,
`${endpoint}/movie/${movieId}/reviews?api_key=${apiKey}`,
`${endpoint}/movie/${movieId}/similar?api_key=${apiKey}`,
];
useEffect(() => {
const fetchData = () => {
setIsLoading(true);
Promise.all(
urls.map((url) => {
return fetch(url);
})
)
.then((response) => {
return Promise.all(response.map((res) => res.json()));
})
.then((data) => {
setMovieDetails(data);
setIsLoading(false);
})
.catch((err) => {
console.log(err);
});
};
fetchData();
}, []);
console.log(movieDetails[0]);
Hello,
I've encountered a problem tha that when i try to fetch the request above when i console.log() it it first returns undefined and then return the desired response.
The response is expected as initially the state is undefined.
During the request also, till the response is unresolved, the process is suspended and the state stays undefined.
A simple solve will be to move the console.log(movieDetails[0]) into the last .then() body or you could write your own Promise resolution functions.
Related
I have an API that sometimes doesn't work. I would like for the App to refetch automaticaly if this happens until it gets the necessary data. How can I do that? I'm thinking that maybe this could be done by using a dependency on the useEffect hook, but I'm not clear on how to do it.
Lets say we have this App component
export default function App() {
const [data, setData] = useState([])
useEffect(() => {
getData({ setData })
}, [])
return [
<h3>
{data[0].title}
</h3>
]
}
And this API component
const url = 'https://some-random-url.com/whatever-api'
export default function getData({ setData }) {
axios.get(url)
.then((response) => {
let dataArray = response.data.results
setData(dataArray)
})
.catch((error) => {
console.log(error)
})
}
If you were to do it with useEffect, you could pass an error counter state to getData function and increase it on error or empty data.
Then add to your useEffect dependency array to refetch.
But this certainly implies that you have to think further what you are wanting to do after a certain amount of retries, to avoid an infinite loop.
export default function App() {
const [data, setData] = useState([])
const [errCount, setErrCount] = useState(0)
useEffect(() => {
getData({ setData, errCount, setErrCount })
}, [errCount])
return [
<h3>
{data[0].title}
</h3>
]
}
And this API component
const url = 'https://some-random-url.com/whatever-api'
export default function getData({ setData, errCount, setErrCount }) {
axios.get(url)
.then((response) => {
let dataArray = response.data.results
setData(dataArray)
!dataArray.length && setErrCount(errCount+1);
})
.catch((error) => {
setErrCount(errCount+1);
console.log(error)
})
}
I am relatively new to react hooks and I am trying to create this custom hook to handle CRUD operations for my API.
This is the hook file:
import React, { useState, useEffect } from "react";
const useApi = (url, headers = { method: "GET" }, payload = null) => {
const [isLoading, setIsLoading] = useState(true);
const [apiData, setApiData] = useState(null);
const [serverError, setServerError] = useState(null);
const [api, setApi] = useState({});
const list = async () => {
try {
const resp = await fetch(url);
const data = await resp?.json();
setApiData(data);
setIsLoading(false);
} catch (error) {
setServerError(error);
} finally {
setIsLoading(false);
}
};
const create = async () => {
try {
const resp = await fetch(url, (headers = { method: "POST" }), payload);
const data = await resp?.json();
setApiData(data);
setIsLoading(false);
} catch (error) {
setServerError(error);
} finally {
setIsLoading(false);
}
};
setApi({
...api,
list: list,
create: create
});
return { isLoading, apiData, serverError, api };
};
export default useApi;
However, when I call api.list() in my main component inside a useEffect() hook, I get an infinite loop.
Sample component call:
import { useEffect } from "react";
import useApi from "./useApi";
export default function App() {
const {
isLoading: loading,
apiData: students,
serverError: error,
api
} = useApi("https://59f0f160ce72350012bec011.mockapi.io/students");
console.log(loading, students, error, api);
useEffect(() => {
api.list();
}, [api]);
return (
<div className="App">
<h1>list</h1>
{loading ? "loading" : students.map((x) => x.name)}
</div>
);
}
Here's the sandbox for it:
https://codesandbox.io/s/cocky-chebyshev-d9q89?file=/src/App.js:0-492
Can anyone help me understand the issue?
Thank you in advance!
This is what is causing the infinite loop:
setApi({
...api,
list: list,
create: create
});
You are not supposed to call setState() during a render.
In your case, you don't need to useState for the api object, you can just return it on every render:
return {
isLoading,
apiData,
serverError,
api: { list, create }
};
Here is a link to the fixed sandbox
Also, another warning: this code will repeatedly call api.list().
useEffect(() => {
api.list();
}, [api]);
Since api changes on every render, it will repeatedly call api.list().
This is the object that changes on every render:
return { isLoading, apiData, serverError, api };
You can ensure that you only call api.list() one time by using a ref.
import { useRef } from 'react'
// In the component
const gotRef = useRef(false)
useEffect(() => {
if (!gotRef.current) {
api.list();
gotRef.current = true
}
}, [api]);
Below committed code is axios. Which is working fine. But javascript fetch is not working. Let me know what is the issue in the code. How to fix it. Please explain it in a simple way. Thanks!
import React, {useEffect, useState} from 'react'
import axios from 'axios'
function Datafetching() {
const [posts, setposts] = useState([])
const getMovieRequest = async () => {
const url = `http://jsonplaceholder.typicode.com/posts`;
const response = await fetch(url);
const responseJson = await response.json()
if (responseJson.data){
setposts(responseJson.data)
console.log(responseJson.data)
}
}
useEffect(() => {
// axios.get('http://jsonplaceholder.typicode.com/posts')
// .then(res => {
// console.log(res)
// setposts(res.data)
// })
// .catch(err => {
// console.log(err)
// })
getMovieRequest()
})
return (
<div>
<ul>{
posts.map(post => <li key={post.id}>{post.title}</li>)
}
</ul>
</div>
)
}
export default Datafetching
responseData contains the data as array, not responseData.data:
if (responseJson){
setposts(responseJson)
console.log(responseJson)
}
I am new with react hooks, i'm trying to get info from an API but when i do the request i get 2 responses first an empty array and then the data of the API, why am i getting that empty array! , this is my first question, i'm sorry.
Thanks for helping me !
import {useState, useEffect} from 'react';
const getSlides = (API) => {
const[data,setData] = useState([]);
const getData = () =>
fetch(`${API}`)
.then((res) => res.json())
useEffect(() => {
getData().then((data) => setData(data))
},[])
return data
}
export default getSlides;
The useEffect() hook runs after the first render. Since you've initialized the data state with an empty array, the first render returns an empty array.
If you're component depends on data to render, you can always conditionally return null until your data is loaded.
Also, I recommend using an async function for api requests, it allows you to use the await keyword which makes your code easier to read. The only caveat, is that you cannot pass an async function to useEffect, instead define an async function inside your hook, and then call it.
import React, { useState, useEffect } from "react";
const API = "https://example.com/data";
const GetSlides = (props) => {
const [data, setData] = useState();
useEffect(() => {
async function getData() {
const request = fetch(API);
const response = await request;
const parsed = await response.json();
setData(parsed);
}
getData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (data === undefined) {
return null;
}
return <>data</>;
};
export default GetSlides;
Of course, you can still use Promise chaining if you desire.
useEffect(() => {
async function getData() {
await fetch(API)
.then((res) => res.json())
.then((data) => setData(data));
}
getData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
<GetSlides api="https://yay.com" />
react components need to be title case
import React, { useState, useEffect } from 'react'
const GetSlides = ({ api }) => {
const [data, setData] = useState(null)
const getData = async () =>
await fetch(`${api}`)
.then((res) => res.json())
.then((data) => setData(data))
useEffect(() => {
getData()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
console.log(data)
return <div>slides</div>
}
export default GetSlides
The effect callback function is called after the render of your component. (Just like componentDidMount) So during the first render phase, the data state has not been set yet.
You initialize your data with and empty array here:
const[data,setData] = useState([] <- empty array);
useEffect runs after your component is mounted, and then calls the API, that it might take a few seconds or minutes to retrieve the data, but you return the data right away before knowing if the API finished its call.
If you want to return the data after it has been retrieved from the API, you should declare and async method
const getSlides = async (API) => {
try {
const res = await fetch(API);
const data = await res.json();
return data;
} catch (e) {
throw new Error(e);
}
}
Note that it is not necessary hooks for this function
I am trying to import a function that fetches data from an api in a file (.src/api/index.js) to my App.js (.src/App.js).
.src/api/index.js
import axios from 'axios';
const url = 'https://covid19.mathdro.id/api';
export const fetchData = async () => {
try {
const res = await axios.get(url);
return res;
} catch (error) {}
};
.src/App.js
import React, { useEffect } from 'react';
import { fetchData } from './api';
const App = () => {
useEffect(() => {
const data = fetchData();
console.log(data);
}, []);
return <div></div>;
};
export default App;
};
I am getting a Promise{<pending>} in my console when I run this but I am trying to get the values in the object.
fetchData() is an async function, and you need to await it like so:
const data = await fetchData();
Then, the useEffect must also be an async function:
useEffect(async () => {
const data = await fetchData();
console.log(data);
}, []);
You are not waiting for promise to resolve. use await or .then. If you wanna use await, make callback function of useEffect async function.
const App = () => {
useEffect(async () => {
const data = await fetchData();
console.log(data);
}, []);
return <div></div>;
};
Other approach is to use .then.
const App = () => {
useEffect(async () => {
const data = fetchData().then((data) => console.log(data));
}, []);
return <div></div>;
};