I am working on a simple crypto ticker app . I am using an api to get details about cryptos and this is how fetching look likes currently.
const [ Data , setData ] = useState([]);
useEffect(() => {
const interval = setInterval(() => {
fetch('https://api.coindcx.com/exchange/ticker').then((result) => {
result.json().then((resp) => {
console.log(resp)
setData(resp);
console.log(Data)
setLoading(false);
})
})
}, 3000);
return () => clearInterval(interval);
},[])
I am using interval so that data is updated every 3 seconds.
The problem I am facing is that in the useEffect setData is not setting data. console.log(resp) is working correctly but console.log(Data) is loggin out an empty array in console whereas I want data and resp to be the same.
If you want to console.log your data
You need to use useEffect for console.log, because setstate in async and not sync
useEffect(() => {
const interval = setInterval(() => {
fetch('https://api.coindcx.com/exchange/ticker').then((result) => {
result.json().then((resp) => {
console.log(resp)
setData(resp);
console.log(Data)
setLoading(false);
})
})
}, 3000);
return () => clearInterval(interval);
},[])
and than:
useEffect(() =>
{
console.log(data)
}
,[data])
Related
The below function gets the current location of a user:
const getCurrentLocation = () => {
fetch("https://ipinfo.io/json?token=$TOKEN")
.then((response) => response.json())
.then((jsonResponse) => {
console.log(jsonResponse)
return jsonResponse;
});
};
useEffect(() => {
console.log(getCurrentLocation());
}, []);
logging in useEffect is showing undefined and it is appearing first in the console, then jsonResponse shows next in the console with the corresponding object. Why is that ?
getCurrentLocation doesn't return anything, that's why you got undefined.
Moreover, fetch returns a Promise, which is asynchronous, meaning you don't get the result immediately, you must pass a calback to then to get the result when it is available.
const getCurrentLocation = () => {
return fetch("https://ipinfo.io/json?token=$TOKEN")
.then(response => response.json());
};
useEffect(() => {
getCurrentLocation()
.then(location => console.log(location));
}, []);
The getCurrentLocation function is not returning anything. Try saving the location in the state, so that you can access it when needed:
const [currentLocation, setCurrentLocation] = useState(null);
const getCurrentLocation = () => {
fetch("https://ipinfo.io/json?token=$TOKEN")
.then((response) => response.json())
.then((jsonResponse) => {
setCurrentLocation(jsonResponse); // <- save the location in state
});
};
useEffect(() => {
getCurrentLocation();
}, []);
return <div>{currentLocation}</div>
If you need the location in a useEffect, you could do:
useEffect(() => {
if (currentLocation !== null) {
// ...
}
}, [currentLocation])
you can simply use async/await to get response, take a look at this one:
const getCurrentLocation = async () => {
const result = await fetch("https://ipinfo.io/json?token=$TOKEN");
return result.json();
};
const handleGetLocation = async () => {
const result = await getCurrentLocation();
console.log(result);
};
useEffect(() => {
handleGetLocation();
}, []);
I have a useEffect() that fetches the data through axios, I want that to render only one time so I passed an array. Everything works fine, but the problem is whenever I try to sort the items, the second useEffect fires just followed by the first useEffect, which is causing the component to fetch the items all over again and again.
const [products, setProducts] = useState([]);
useEffect(() => {
const getProducts = async () => {
return await axios
.get('/getAllProducts')
.then((response) => {
setProducts(response.data);
console.log(products);
})
.catch((e) => {
console.log(e);
});
};
getProducts();
}, [products]);
This is because you passed an array containing your products state, rather than an empty array, which will fire useEffect on state change (for products state specifically). Try changing your code to an empty array:
useEffect(() => {
const getProducts = async () => {
return await axios
.get('/getAllProducts')
.then((response) => {
setProducts(response.data);
console.log(products);
})
.catch((e) => {
console.log(e);
});
};
getProducts();
}, []);
As #skyboyer mentioned below, it is good to note that state is not updated in a synchronous manner. Therefor, console.log(products) will not reflect an accurate value for your state when useEffect runs.
It is okay to use multiple useEffect hooks. If you would like to view your updated state in the console, or do some other work with it, you could add another useEffect hook and pass your state into the array:
useEffect(() => {
console.log(products);
}, [products]);
Since products is in the useEffect dependency array, it is going to run every time there are changes made to the products state. getProducts() runs setProducts which then in turn is going to trigger the use effect again. Using an empty array in the useEffect will tell it to only run when the component is mounted.
Like this:
useEffect(() => {
const getProducts = async () => {
return await axios
.get('/getAllProducts')
.then((response) => {
setProducts(response.data);
console.log(products);
})
.catch((e) => {
console.log(e);
});
};
getProducts();
}, []);
I want to call the function getWeather every time setLocation() is called.How can I achieve it?
useEffect(() => {
fetch(`${IP_API_URL}`)
.then((res)=> res.json()
.then((data)=>{
setLocation(data.country.name)
})
)
return getWeather(location)
})
setLocation will have a state right ???
const [location, setLocation] = useState();
useEffect(() => {
fetch(`${IP_API_URL}`)
.then((res) => res.json()
.then((data) => {
setLocation(data.country.name)
})
)
}, [])
//add another effect
useEffect(() => {
getWeather(location)
}, [location])
I am using useEffect to get data from an api.
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const cats = await response.json();
console.log(cats);
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
The problem is when I try to use it in the return, its value is undefined.
{cats.map((data) => {
cats has value when I console.log it.
I cannot use componentDidMount because all my code is functional components.
Edit: I updated the code as per answers below but still get
TypeError: cats.map is not a function
All answers below actually make sense but I am not sure why its not working.
export default function Posts() {
const [cats, setCats] = useState();
useEffect(() => {
fetch(`/api/posts/getCats`)
.then(res => res.json())
.then(setCats)
.catch(console.error);
}, []);
return (
<div>
{cats?.map((data) => {
<h4>{data.main}</h4>
})}
</div>
)
}
This is because React renders your screen before finishing to get response from API. When you render screen, variable cats doesn't have values. You can run useEffect after each rendering. You can rerender by changing state from useEffect (This technique is often used). Do not forget to add [] or [cats] as a dependency of useEffect (second params) otherwise you will get infinite loop.
Below code works even when cats === [] or some array.
export default () => {
const [cats, setCats] = useState([])
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const result = await response.json();
setCats(result)
} catch (e) {
}
};
fetchData();
}, []);
return (
<div>
{cats.map(cat => <div>cat</div>)}
</div>)
}
You have to map the cats data into state.
const [cats, setCats] = useState([]);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const data = await response.json();
setCats(data);
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
You need to
call setCats when the response comes back (right now, you're just logging it)
.map only once cats has been populated:
const [cats, setCats] = useState();
useEffect(() => {
fetch(`/api/posts/getCats`)
.then(res => res.json())
.then(result => setCats(result.cats))
.catch(console.error);
}, []);
return (
<div>
{cats?.map((data) => {
// ...
I have a specific case. The first thing I do is request the Index.DB. After I got the taskId from it, I need to start asking the server every 5 seconds. And stop doing this on a specific flag. How can i do that properly with hooks?
I'tried to use useInterval hook like this:
https://github.com/donavon/use-interval;
But when i set it in useEffect causes consistent error:
Invalid hook call. Hooks can only be called inside of the body of a function component.
const Page = () => {
const [task, setTask] = useState({})
const isLoaded = (task.status === 'fatal');
const getTask = (uuid: string) => {
fetch(`${TASK_REQUEST_URL}${uuid}`)
.then(res => {
return res.json();
})
.then(json => {
setTask(json.status)
})
.catch(error => console.error(error));
};
useEffect(() => {
Storage.get('taskId')
.then(taskId => {
if (!taskId) {
Router.push('/');
}
useInterval(() => getTask(taskId), 5000, isTaskStatusEqualsSomthing)
})
}, []);
return (
<p>view</p>
);
};
I also tried to play around native setInterval like this
useEffect(() => {
Storage.get('taskId')
.then(taskId => {
if (!taskId) {
Router.push('/');
}
setInterval(() => getTask(taskId), 5000)
})
}, []);
But in this case i don't know how to clearInterval and also code looks dirty.
The solution is simple. You just need to configure your setInterval within .then callback like
useEffect(() => {
let timer;
Storage.get('taskId')
.then(taskId => {
if (!taskId) {
Router.push('/');
else {
timer = setInterval(() => getTask(taskId), 5000)
}
}
})
return () => {clearInterval(timer)}
}, []);
The reason, first approach doesn't work for you is because you cannot call a hook conditionally or in useEffect as you did for useInterval