How to fetch repeatedly and render updated api data in reactjs? - javascript

I am trying to render time data from API endpoint http://worldclockapi.com/api/json/utc/now
The "currentFileTime" property is constantly changing but renders once on load.
I tried setInterval method to update state but it doesn't work. May be I am making some mistake?
This is App.js:
import React , { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = { data: []};
}
async componentDidMount(){
this.fetchData();
}
async fetchData() {
try {
const response = await fetch('http://worldclockapi.com/api/json/utc/now');
if (!response.ok) {throw Error(response.statusText);}
const json = await response.json();
this.setState({ data: json});
console.log(json);
}
catch (error) {console.log(error);}
}
render() {return (<div><h1>worldclockapi.com data (edit App.js)</h1>
<li>currentFileTime: {this.state.data.currentFileTime }</li>
</div> );
}
}
export default App;
How to render and update currentFileTime continuously in react component?

the problem is componentDidMount executed only once, after component mounted for the first time, for example if your state changes componentDidMount is not gonna execute again.
in your case i think it's better to use websockets but if u wanna keep useing this api u can use useEffect hook like below:
const [temp, setTemp] = useState(0)
useEffect(()=>{
setIterval(()=>{
setTemp((prevTemp)=>prevTemp+1)
}, 2000)
}, [])
useEffect(()=>{
fetchData()
}, [temp])
in the above code we have a temp variable and it's value update every 2 second and every time it gets updated the second useEffect run and the data will fetch and as a result the state's gonna change and element gets updated.

Try Calling fecthData recursively upon successful data retrieval like below.
And you don't need to put "async" in front of componentDidMount cause you ain't awaiting anything in the method call.
async fetchData() {
try {
const response = await fetch('http://worldclockapi.com/api/json/utc/now');
if (!response.ok) {throw Error(response.statusText);}
const json = await response.json();
this.setState({ data: json});
console.log(json);
// set the time below to how frequently you wanna update
setTimeout(() => this.fetchData(), 5000);
//
}
catch (error) {console.log(error);}
}

This is using the new hooks. This should solve the problems
import React, {useEffect, useState} from 'react';
import logo from './logo.svg';
import './App.css';
const App = () => {
const [state, setState] = useState({data: []});
useEffect(()=>{
fetchData();
}, [state]);
const fetchData = async () => {
try {
const response = await fetch('http://worldclockapi.com/api/json/utc/now');
if (!response.ok) {throw Error(response.statusText);}
const json = await response.json();
setState({ data: json});
console.log(json);
}
catch (error) {console.log(error);}
}
return (<div><h1>worldclockapi.com data (edit App.js)</h1>
<li>currentFileTime: {state.data.currentFileTime }</li>
</div> );
}
export default App;

Related

Getting undefined from state while using useEffect and useState

My issue here is that sometimes my projects state is returning undefined sometimes. I am not sure why. As you can see, in the useEffect I have a function that gets project data from an API call to my backend server. This will then return an array of projects, which I then planned to see in the dom in the return statement. However, for whatever reason, upon the initial render it gives me an undefined and the screen goes white.
Strangely, enough, if I change the return statement to just display a regular string, let's say "hello" for example, save, and then change it back to {projects[0].name} it will then work. Yet on initial render I am getting a Uncaught TypeError: Cannot read properties of undefined (reading 'name');
I will add that I am getting a 304 status from my server in the console but that is because the data has not changed and thus I am receiving the previous UI from local storage if I remember correctly. This is not an issue with other parts of my application so I do not know why it would be an issue here.
import { useEffect, useState } from "react";
import { fetchPage } from "./../store/actions"
import { connect } from "react-redux"
/*import { ProjectCard } from "./../components/ProjectCard"*/
import API from './../api/API'
const Projects = ({ fetchPage }) => {
const [projects, setProjects] = useState([])
useEffect(() => {
const getProjectData = async () => {
try {
const { data } = await API.getAllProjects()
setProjects(data.data)
} catch (err) {
console.log(err);
}
}
fetchPage('Projects', "Here are your projects")
getProjectData()
}, [fetchPage])
return (<div>
{projects[0].name}
</div>)
}
export default connect(null, { fetchPage })(Projects);
Here is a different part of my application that works more or less the same way
const [users, setUsers] = useState([])
useEffect(() => {
const getUserData = async () => {
const { data } = await axios.get('/api/v1/users', {
headers: {
'Content-type': 'application/json'
}
})
setUsers(data.data.data)
}
fetchPage("TEAM", "Here is your team");
getUserData();
}, [fetchPage])
I tried removing the action creator which I expected did not work

I got products in console in redux actions but When i use same funtion in component and try to get products by useselector hook i got undefined

This is an API call and in console, i get all products . But when I use the same getProducts function in components I got undefined in console
export const getProducts = ()=> async(dispatch)=>{
try {
const data = await fetch("http://localhost:80/api/products/getallproducts",{
method:"GET",
headers:{
"Content-Type":"application/json"
}
});
const res = await data.json();
console.log(res);
dispatch({type:"SUCCESS_GET_PRODUCTS",payload:res});
} catch (error) {
dispatch({type:"FAIL_GET_PRODUCTS",payload:error.response});
}
}
I use it on Home page and got undefined instead of products as i am using same function of getProducts
import React, { useEffect } from 'react'
import Categories from '../components/Categories'
import Banner1 from '../components/Banner1'
import MaterialUiaresoul from '../components/MaterialUiaresoul'
import ProductSlide from '../components/ProductSlide'
import FeaturedProducts from '../components/FeaturedProducts'
import { useDispatch, useSelector } from 'react-redux'
import { getProducts } from '../redux/actions/action'
const Home = () => {
const products = useSelector(state => state.getproductsdata);
console.log(products)
const dispatch = useDispatch();
useEffect(() => {
dispatch(getProducts());
}, [dispatch]);
return (
<>
<MaterialUiaresoul/>
<ProductSlide/>
<Banner1/>
<Categories/>
<FeaturedProducts />
</>
)
}
export default Home
You are trying to dispatch something that is not redux action.
Let's see, you are trying to call this line dispatch(getProducts());
After getProduct call, it will return a new async function, that doesn't called and expect dispatch to be passed in it.
Normally actions look like this:
export function addTodo(text) {
return { type: ADD_TODO, text }
}
Its just a function that return a plain object with type as a required property.
When dealing with api calls using redux, its better to look into some libraries that will help you, such as redux-thunk or redux-saga for example. Redux actions sync by default and async behavior can be reached with use of some middlewares.
In your example, you can make your code work as expected if you will run your getProduct function, and then run response from it with dispatch passed as first argument:
const dispatch = useDispatch();
const createApiCall = getProduct();
createApiCall(dispatch)
I'm still not sure whether it will work and recommend you to look at redux-thunk. Its pretty easy to learn and use.

Async function in UseEffect

I am trying to get data from AsyncStorage and eventually map this data to a list of buttons on my home screen. The data is saved to AsyncStorage from an API call that is made upon login.
In my async function, I am able to successfully retreive the data from AsyncStorage and parse it into JSON format, and then log it to the console. It looks like this:
{
1 : {title:"Timesheet",component_name:"Timesheet"}
2 : {title:"Personal Info",component_name:"PersonalInfo"}
3 : {title:"Employee Directory",component_name:"EmployeeListing"}
}
The problem I am running into is that I can't save this data to my useState variable and then render it into the component after useState is updated by my async function. Every time I try to access this data, I either get null or a Promise object. How can I access the data after useState is updated? Do I need to use a different React hook to call the Async function?
Here is the code that I am using:
import { Text, View, StyleSheet } from 'react-native';
import { useState, useEffect } from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage';
export default function HomeScreen() {
const [buttonData, setButtonData] = useState(null);
useEffect (() => {
const loadHomeScreenButtons = async () => {
try {
const buttons = await AsyncStorage.getItem('app_screens').then(screens => {
// Parse the JSON data from its stored string format into an object.
let app_screens_json = JSON.parse(screens);
let app_screens_list = app_screens_json.app_screens;
console.log(app_screens_list); // This outputs the data to the console.
setButtonData(app_screens_list); // Trying to set the button data in useState.
return app_screens_list;
});
}
catch (e) {
console.log(e)
}
}
loadHomeScreenButtons();
}, [])
return (
<View style={home_styles.container}>
<Text>{buttonData[1]["title"]}</Text>
</View>
);
}
You just need to render a loading component until your data is fetched.
{ buttonData?.length?
<Text>{buttonData[1]["title"]}</Text> : <Text>...loading</Text>
}
You are getting an error as you are trying to access a property that does not exist at the render.
Try this way
const loadHomeScreenButtons = useCallback(async () => {
try {
const screens = await AsyncStorage.getItem('app_screens')
const app_screens_json = JSON.parse(screens)
setButtonData(app_screens_json.app_screens)
}
catch (e) {
console.log(e)
}
}, []);
useEffect(() => {
loadHomeScreenButtons();
}, [loadHomeScreenButtons]);

Axios request keeps returning twice undefined and twice the data

I'm trying to fetch an api on a custom reactjs hook using Axios. I keep getting twice the response as undefined and after that twice as a successful fetch with the data. The undefined breaks my app.
Btw I'm fetching from the randomuser api.
import axios from "axios";
import { useState, useEffect } from "react"
export const useFetch = (url) => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
const [error, setError] = useState('')
const getData = () => {
setLoading(true)
try {
axios.get(url)
.then(response => setData(response.data));
setLoading(false)
} catch (error) {
setError(error)
}
};
useEffect(() => {
getData()
}, [url])
return {loading, data, error}
}
Trying to use it here and map over it
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useFetch } from '../custom_hooks/useFetch';
const PersonDetails = () => {
const { loading, data , error } = useFetch('https://randomuser.me/api?results=20');
const { results } = data;
const { id } = useParams();
const [person, setPerson] = useState({})
useEffect(() => {
const newPerson = results?.find(person => person.login.uuid === parseInt(id))
setPerson(newPerson)
console.log(newPerson)
}, [])
return (
<div>
{person.name.first}
</div>
)
}
export default PersonDetails
This is the thing I actually Im trying to do, but now because it is undefined, I get that cannot read properties of undefined...
When the effect runs you:
setLoading(true)
Send the Ajax request
setLoading(false)
Later, then the Ajax response arrives you:
setData(response.data)
Since you depend on loading to determine if data is set or not, it breaks.
There are two things you could do:
Move setLoading(false) inside the then callback so it doesn't get set until after you have setData(response.data)
Get rid of loading entirely and base your logic off data being undefined or having a different value.
you should define the getData function inside the useeffect or pass it in dependency array and wrap the function by usecallback to avoid unnecessary rerenders.
you should use abortcontroller in case of cancelations and to have cleanup function in useeffect. (in this case it's better to define getdata body in useeffect)
useEffect(() => {
const controller = new AbortController();
const getData = async () => {
setLoading(true)
try {
await axios.get(url, {signal: controller.signal})
.then(response => setData(response.data));
} catch (error) {
setError(error)
}
}
getData()
return()=>controller.abort()
},[url]}
you can read more about fetching data with hooks in following url and where to setloading and other needed states.
https://www.robinwieruch.de/react-hooks-fetch-data/
Just in case, this solution helped me : https://github.com/axios/axios/issues/2825#issuecomment-883635938
"The problem in my case was caused by React development server.
The strict mode in react caused the issue!
I had to remove the strict mode
This solved the problem of sending double requests!
The strict mode checks are only run in development mode.
Doc: https://reactjs.org/docs/strict-mode.html
"

Not able to use value returned by React hook

I am trying to use a custom hook to make HTTP requests and then use a reducer to update the state in the component.
The hook runs correctly and I can get the response from the request but not able to use the response data in dispatch function.
Below is the code:
HTTP hook:
import React, { Fragment, useState, useEffect, useReducer } from 'react';
import axios from 'axios';
export const useHttpRequest = (initialData, initialURL) => {
const [url, setUrl] = useState(initialURL);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchData = async () => {
console.log('in a http hook');
setIsError(false);
try {
const res = await axios(url);
console.log(res);
const responseData = res.data.data.data;
return responseData;
} catch (error) {
setIsError(true);
}
};
fetchData();
}, [url]);
return { isError, setUrl };
};
A function call in the state:
const { isError, setUrl } = useHttpRequest();
const getCategoryData = async () => {
setLoading();
try {
const Data = await setUrl('/api/v1/category');
dispatch({
type: SET_CATEGORYDATA,
payload: Data
});
} catch (err) {}
};
A function call in components, where the function is passed through useContext
useEffect(() => {
getCategoryData();
}, []);
You cannot await on a setState (setUrl) function.
You return in your fetch data which is not used later.
You need to first change your mindset on how you think in react hooks and when you need to use them.
As far as I understand you want to fetch some data from server, update the store on successful retrieval, and show an error when the request fails.
You should do this all the way or don't do this at all. You can put the dispatch in the hook or you can forget about the hook and write a reusable fetchData function and handle setHasError in your component's useEffect.
There are many ways to solve this but this is my preferred solution:
import React, { Fragment, useState, useEffect, useReducer } from 'react';
import axios from 'axios';
export const useHttpRequest = (url, updateStore) => {
const [hasError, setHasError] = useState(false);
const fetchData = async (url) => {
setHasError(false);
try {
const res = await axios(url);
const responseData = res.data.data.data;
updateStore(responseData);
} catch (error) {
setHasError(true);
}
};
useEffect(() => {
if (url) {
fetchData(url);
}
}, [url]);
return { fetchData, hasError };
};
// in case you want to fetch the data on component render
const { fetchData, hasError } = useHttpRequest(url, (data) => dispatch({
type: SET_CATEGORYDATA,
payload: data
}));
// in case you want to fetch it in a callback
const clickButton = () => {
fetchData(someCustomUrl);
}
Finally, you can generalize your dispatchers so you don't need to send the whole function to the hook and only send the dispatcher name.

Categories

Resources