hello fetch my data is making more than one request, why? - reactjs

I left the code below that I got my data from. More than one request is processed at the time of refreshing the page, the reason may be why, if you can help I would appreciate it. have a nice day.
import React, { useEffect, useState } from "react";
import axios from "axios"
import Cookies from "universal-cookie"
const Entry = React.createContext();
export const EntryProvider = ({ children }) => {
const [post, setPost] = useState();
const cookie = new Cookies()
const token = cookie.get("acsess_token")
const getAll = () => {
axios.defaults.headers.common['Authorization'] = token;
const entry = axios.get("/api/entry/entry", {
headers: {
"Authorization": token
}
})
.then((response) => {
const data = response.data.data
data.map(element => {
setPost(element)
});
setPost(data)
})
.catch((err) => { console.log(err) })
}
useEffect(() => {
getAll()
},[getAll])
return (
<Entry.Provider value={{post}}>
{children}
</Entry.Provider>
);
};
export const userEntry = () => {
return React.useContext(Entry);
};

Instead adding getAll in the array dependency, remove it
useEffect(() => {
getAll()
},[getAll])
Like this:
useEffect(() => {
getAll()
},[])
Why that?
Because the useEffect will be execute it every time the component renders and because of having getAll in the dependency array it will execute it again

Related

How to keep calling api until task is completed in reactjs?

I am working on reactjs as frontend, and Django in the backend.
I have a time taking task written in django, which ideally takes more time to retrieve values than the stipulated API response time.
Therefore, I have made it into a celery task, whose task id I return as an API response.
The plan was to make the API call on page load which starts the celery task, and returns the task ID. So, with the task ID, I can keep polling another API to get the task's status, until completed. Once the task is completed, I can ping another API to get the response of the celery task.
I thought, I can make the API call, and thenafter run a loop with a sleep, but not sure how to achieve this?
import { useEffect, useState } from "react"
import axios from "axios"
function App() {
const [taskId, setTaskId] = useState("")
const apiToSpawnTask = () => {
axios.get("http://localhost:8000/spawn_task")
.then(({data}) => setTaskId(data.task_id))
}
const checkTaskStatus = () => {
axios.get(`http://localhost:8000/task-id/${taskId}`)
.then(({data}) => {
// data.status contains the status of the task id
})
}
const getCompletedTaskResult = () => {
axios.get(`http://localhost:8000/get-task-result/${taskId}`)
.then(({data}) => {
// this data is used in the return
})
}
useEffect(() => {
// What should be the code here?
})
return (<div>Test</div>)
}
const checkTaskStatus = () => {
return axios.get(`http://localhost:8000/task-id/${taskId}`)
.then(({data}) => {
return data
})
}
// ...
useEffect(() => {
const interval = setInterval(() => {
const status = checkTaskStatus
// probably a different property returned from your api
if (status.ready) {
clearInterval(interval)
}
}, 1000)
return () => clearInterval(interval)
})
import { useEffect, useState, useRef } from "react"
import axios from "axios"
function App() {
const [taskId, setTaskId] = useState("");
const intervalRef = useRef(null);
const apiToSpawnTask = () => {
axios.get("http://localhost:8000/spawn_task")
.then(({data}) => {
setTaskId(data.task_id);
intervalRef.current = setInterval(() => {
checkTaskStatus(data.task_id);
}, 5000);
})
}
const checkTaskStatus = (id) => {
axios.get(`http://localhost:8000/task-id/${id}`)
.then(({data}) => {
// data.status contains the status of the task id
if(data.status === 'success') {
getCompletedTaskResult();
clearInterval(intervalRef.current);
}
})
}
const getCompletedTaskResult = () => {
axios.get(`http://localhost:8000/get-task-result/${taskId}`)
.then(({data}) => {
// this data is used in the return
})
}
useEffect(() => {
apiToSpawnTask();
}, [])
return (<div>Test</div>)
}
Tip: instead of hardcoding the base URL as http://localhost:8000, try to use axios instance for that. So if you want to change base URL in future, you don't need to modify everywhere.
axios/index.js
import axios from "axios";
const instance = axios.create({
baseURL: "http://localhost:8000"
});
export default instance;

React useEffect fetch data and print it out via the conosle

I want to fetch data from an API and then print it out or to display it in the return statement from my react compenent so I can have a p element that have data fetched from the api in it.
The problem is that the usestate dont get updated
The component code
import React, { useEffect } from "react"
import { newsComponentService } from "../services/newsComponentService";
const NewsComponent = () => {
const [newsComponentData, setNewsComponentData] = React.useState({});
const componentData = React.useRef("");
async function newsComponentHandler() {
let res = await newsComponentService();
//console.log(res);
setNewsComponentData(res);
//console.log(res);
}
useEffect(() => {
newsComponentHandler();
//setNewsComponentData(res);
}, [setNewsComponentData]);
console.log(newsComponentData["data"]);
return (
<p>{newsComponentData["data"]}</p>
)
}
export default NewsComponent;
The api service code
export async function newsComponentService(){
const response = await fetch("api/news-categories/1", {
method: 'GET',
headers: {
'Accept': 'application/json',
},
});
let resJson = await response.json();
//console.log(resJson);
return resJson;
}
I think the issue could be with the async behavior of the JS.
import React, { useEffect } from "react"
import { newsComponentService } from "../services/newsComponentService";
const NewsComponent = () => {
const [newsComponentData, setNewsComponentData] = React.useState({});
const componentData = React.useRef("");
useEffect(() => {
const newsComponentHandler = async () => {
let res = await newsComponentService();
setNewsComponentData(res);
}
newsComponentHandler();
}, [setNewsComponentData]);
console.log(newsComponentData["data"]);
return (
<p>{newsComponentData["data"]}</p>
)
}
export default NewsComponent;
PS. As a good practice, please put the API fetching in try-catch block in newsComponentService.js

How can I add sorting using Redux?

How can I add sorting for API returning a JSON array? I'm new to Redux. I have installed the redux. Could someone tell me what's the best method to follow?
Thanks for your help.
import React, { useState, useEffect } from "react";
import Post from "../../Components/Post/Post";
import axios from "axios";
const HomePage = () => {
const [posts, setPosts] = useState("");
let config = { Authorization: "............." };
const url = "..........................";
useEffect(() => {
AllPosts();
}, []);
const AllPosts = () => {
axios
.get(`${url}`, { headers: config })
.then((response) => {
const allPosts = response.data.articles;
console.log(response);
setPosts(allPosts);
})
.catch((error) => console.error(`Error: ${error}`));
};
return (
<div>
<Post className="Posts" posts={posts} />
</div>
);
};
export default HomePage;
You don't have redux here. Do you need it?
If you want to sort result and save sorted results to state:
...
.then((response) => {
const allPosts = response.data.articles;
// sort the result here
const sortedPosts = allPosts.sort((a,b) =>
// comparer
);
setPosts(sortedPosts);
})
...

setState react hook for array is not saving prior array elements

I have an API request that uses aysnc and await, grabs the data, then makes a second request with Promise.all, which makes multiple API requests with the id's. That part works out fine.
However, when I go to save the data inside a React hook called, "setItem", it only saves that one and over writes the others. I have a spread operator inside the setItem()
setItems(...items, data)
data being the response from the API request.
My API request is in the top layer of my react app, so I pulled it out into it's own little helper file, that's why "items" and "setItems", are arguments passed through.
import axios from 'axios';
import BottleNeck from 'bottleneck'
const limiter = new BottleNeck({
maxConcurrent: 1,
minTime: 333
})
export const Request = (items, setItems) => {
const getData = () => {
const options = 'newstories'
const API_URL = `https://hacker-news.firebaseio.com/v0/${options}.json?print=pretty`;
return new Promise((resolve, reject) => {
return resolve(axios.get(API_URL))
})
}
const getIdFromData = (dataId) => {
const API_URL = `https://hacker-news.firebaseio.com/v0/item/${dataId}.json?print=pretty`;
return new Promise((resolve, reject) => {
return resolve(axios.get(API_URL))
})
}
const runAsyncFunctions = async () => {
const {data} = await getData()
Promise.all(
data.map(async (d) => {
const {data} = await limiter.schedule(() => getIdFromData(d))
//****************** issue here ************************//
setItems([...items, data]);
})
)
}
runAsyncFunctions()
}
just in case you want to see the app.js file for reference
import React, { useState, useEffect } from 'react';
import './App.css';
import { SearchBar } from './search-bar';
import { Results } from './results';
import { Request } from './helper/request'
function App() {
const [input, setInput] = useState('');
const [items, setItems] = useState([]);
const handleChange = val => {
setInput(val)
}
// console.log(input)
// console.log(results)
// API calls
// call useEffect here, calls Request(), put results in useEffect
useEffect(() => {
Request(items, setItems)
}, [])
return (
<div className="App">
<SearchBar handleChange={handleChange}/>
<Results items={items} />
</div>
);
}
export default App;
At your Promise.all return each data, after you can chain with then that passes an array with all resolved data. This way you only need to call it once setItems:
Promise.all(
data.map(async (d) => {
const { data } = await limiter.schedule(() => getIdFromData(d));
return data;
})
).then((dataResults) => setItems((results) => [...results, ...dataResults]));

React effect infinite re-renders when fetching data

I want to fetch an array from the backend using a Provider, Context and useEffect:
import React, {useState, useEffect} from 'react'
const UsersContext = React.createContext()
const fetchUsers = async () => {
const url = 'http://localhost:3000/users'
const response = await fetch(url)
console.log('response', await response.json())
return response
}
export const UsersProvider = ({children}) => {
// state
const [users, setUsers] = useState([])
// query data
const data = fetchUsers()
console.log('data', data)
// component updates
useEffect(() => {
if (data) {
// setUsers(data)
}
}, [data])
return (
<UsersContext.Provider value={users}>
{children}
</UsersContext.Provider>
)
}
If I set the users once I have the data back from the backend, I get infinite re-render. The issue is, data is always a promise, although I can see the response after the call is being made:
In the fetchUsers method:
console.log('response', await response.json())
{users: Array(1)}
users: Array(1)
0:
created_at: "2019-10-09T17:41:21.818Z"
email: "ash#email.com"
id: 1
name: "ash"
password_digest: "dafasfae"
updated_at: "2019-10-09T17:41:21.818Z"
In the UsersProvider:
console.log('data', data)
Promise {<pending>}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: Response
body: (...)
bodyUsed: true
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: "OK"
type: "cors"
url: "http://localhost:3000/users"
__proto__: Response
I would move the fetchUsers function inside the effect and try like this:
import React, { useState, useEffect } from "react";
const UsersContext = React.createContext();
export const UsersProvider = ({ children }) => {
// state
const [users, setUsers] = useState([]);
// component mounts
useEffect(() => {
const fetchUsers = async () => {
const url = "http://localhost:3000/users";
const response = await fetch(url);
const usersData = await response.json();
setUsers(usersData);
};
fetchUsers();
}, []);
return (
<UsersContext.Provider value={users}>{children}</UsersContext.Provider>
);
};
Here is a very good post about fetching data with react hooks:
https://www.robinwieruch.de/react-hooks-fetch-data
Your data fetch needs to happen inside the useEffect call. What's happening right now is every time your component renders, you are re-fetching the list of users which causes the data object to change and triggers a re-render. The code below will fetch the data only when the component mounts.
useEffect(() => {
let canceled = false;
const fetchData = async () => {
const users = await fetchUsers();
if (!canceled && data) {
setUsers(data);
}
};
fetchUsers();
return () => { canceled = true; }
}, []);
return only data
const fetchUsers = async () => {
const url = 'http://localhost:3000/users'
const response = await fetch(url)
.then( data => {
return data
})
.catch(err=>console.log(err));
return response;
}
hope this will help you.

Resources