I'm trying to use the React Hook called useEffect to update in my functional component.
I need to pass the CITY_ID, but I can't figure out where to put it.
I have this so far:
const { zooData, setZooData } = useState();
const fetchAnimalData = async (id) => {
const result = await axios("api/animal/" + id);
return result;
};
useEffect(() => {
async function fetchData() {
const response = await fetchAnimalData(CITY_ID);
setZooData(response);
}
fetchData();
}, [CITY_ID]);
And then later, I want to use it like this:
newZooAnimal = {zooData};
But it keeps telling me that "CITY_ID" is not defined.
Is there a proper place to define or put it?
Thanks!
Related
I have created a custom hook that fetches setting from an api that uses Async-Storage.
// Takes the key/name of the setting to retrieve
export const useSettings = (key) => {
// stores the json string result in the setting variable
const [setting, setSetting] = useState("");
const deviceStorage = useContext(DeviceStorageContext);
useEffect(() => {
getValue()
.then(value => setSetting(value));
}, []);
// gets value from my custom api that uses Async-Storage to handle r/w of the data.
const getValue = async () => await deviceStorage.getValueStored(key);
const setValue = async (value) => {
await deviceStorage.setValueStored(key, value);
getValue().then(value => setSetting(value));
};
const removeValue = async () => { }
return [setting, { setValue,removeValue }];
};
This works as expected in Main.jsx without any problem.
const Main = () => {
const [units, operations] = useSettings('units');
useEffect(() => {
const initSettings = async () => {
if (units) {
console.log(units)
return;
}
await operations.setValue({ pcs: 1, box: 20 });
};
initSettings();
}, []);
However, when I even just call the useSetting hook in Form.jsx and visit the page, it freezes my entire app to just that page.
const FormView = ({ handleReset, handleSubmit }) => {
const [setting,] = useSettings('units');
Removing the useState and useEffect fixes it and calling these methods directly works but I really don't want to call getValue() throughout my project and use async/await code to handle it.
Stuck on this for hours now. Any help will be appreciated.
Thank you.
It was a dropdown component library inside FormView that was messing it up. Removing that library fixed it.
I have the following code and I wonder how I can improve performance, specifically, should I move the const fuse = new Fuse... section and the buildSearchRequest function within useEffect so it is called only when the search query is changed? I have noticed my code that consumes the custom hooks hits the new Fuse section many times.
const [searchResults, setSearchResults] = React.useState([])
const fuse = new Fuse(DummySearchResponse.results, {
keys: ["data.programmeTitle"],
includeScore: true,
threshold: 0.2,
})
const searchApiUrlStart = "http://mimir.prd.oasvc.itv.com/search?query="
const searchApiUrlEnd =
"&entityType=programme&streamingPlatform=itv_hub&checkAvailability=true"
const buildSearchRequest = (searchString) => {
return (
searchApiUrlStart +
encodeURIComponent(searchString) +
searchApiUrlEnd
)
}
React.useEffect(() => {
if (!query) return
const fetchData = async () => {
let searchData
if (useLiveSearchApi) {
const liveResponse = await fetch(
"http://mimir.prd.oasvc.itv.com/search?query=" +
buildSearchRequest(query) +
"&entityType=programme&streamingPlatform=itv_hub&checkAvailability=true"
)
const liveJson = await liveResponse.json()
const liveResults = await liveJson.results
searchData = liveResults
} else {
const fuseResponse = await fuse.search(query)
const fuseJson = await fuseResponse.map((result) => {
return result.item
})
searchData = fuseJson
}
const mappedResults = await searchData.map((searchItem) => ({
title: searchItem.data.programmeTitle,
contentImageUrl: searchItem.data.imageHref,
programmeCCId: searchItem.data.programmeCCId,
episodeId: searchItem.data.episodeId,
}))
setSearchResults(mappedResults)
}
fetchData()
}, [query])
return { searchResults }
}```
First and foremost, avoid declaring a callback within the useEffect. What you need to do is use the useCallBack hook to declare your fetchData callBack
Your code should atleast look like...
const fetchData = useCallBack(() => {
// Your Fetch Data Code
}, [<dependency array>]) // Here, you want to add all dependencies whose current state you need. Note that if a dependency is not added here, and you use it within the useCallBack, you'll only access a stale state (state during initialization), and never the updated dependency state.
// Here are three ways you can declare your useEffect
useEffect(fetchData, [query]); You probably wanna use this one. Less lines and much cleaner.
useEffect(() => fetchData(), [query]);
useEffect(() => {
fetchData();
}, [query]);
Your useEffect will only be called once the query variable has been updated. So, to ensure a sideEffect is not triggered, ensure your useEffect or the useCallBack specified as the trigger does not update the variable. If this is the case, your code will be stuck in an indefinite loop.
How can I wait for the url hook to not be empty? handleViewSheet is an on click function and I do not want to use useEffect because of the initial render. Can this be done without useEffect?
const [url, setUrl] = useState('')
const handleViewSheet = () => {
GetSheet(id).then((res) => {
if (res) {
setUrl(res);
}
});
const task = getDocument(url); // url is still empty at this point
}
It seems like a job for useEffect to be honest, so I would recommand to consider using it (You can handle the first render within the useEffect if you want).
But if you still want to avoid using useEffect, you can use async/await to wait for the promise to finish before using url, along with the setUrl call. Something like this:
const [url, setUrl] = useState('')
const handleViewSheet = async () => {
let resUrl = await GetSheet(id).then((res) => {
if (res) {
setUrl(res);
return res;
}
return '';
});
const task = getDocument(resUrl );
}
I'm using a custom hook to get pull some data in from an API for use across a set of React function components. However, esLint throws up a lovely warning:
React Hook useEffect has a missing dependency: 'fetchFromAPI'. Either
include it or remove the dependency array.
I didn't think it's a dependency, as it's inside useFetch() itself. I need to do it as I'm using await. What am I doing wrong? Is it ok to just turn off the warning for this line? Or is there a more canonical syntax I should be using?
function useFetch (url) {
const [data, setData] = useState(null);
async function fetchFromAPI() {
const json = await( await fetch(url) ).json();
setData(json);
}
useEffect(() => {fetchFromAPI()},[url]);
return data;
};
export {
useFetch
};
I suggest you to define async function inside useEffect itself:
function useFetch (url) {
const [data, setData] = useState(null);
useEffect(() => {
async function fetchFromAPI() {
const json = await( await fetch(url) ).json();
setData(json);
}
fetchFromAPI()
},[url]);
return data;
};
You can take a look at valid example from doc faqs which uses async function inside useEffect itself:
function ProductPage({ productId }) {
const [product, setProduct] = useState(null);
useEffect(() => {
// By moving this function inside the effect,
// we can clearly see the values it uses.
async function fetchProduct() {
const response = await fetch('http://myapi/product' + productId);
const json = await response.json();
setProduct(json);
}
fetchProduct();
}, [productId]); // ✅ Valid because our effect only uses productId
// ...
}
Declare it outside your custom effect passing url as parameter and return the json to be setted inside useEffect
async function fetchFromAPI(url) {
const json = await( await fetch(url) ).json();
return json
}
function useFetch (url) {
const [data, setData] = useState(null);
useEffect(() => {
setData(fetchFromAPI(url))
},[url]);
return data;
};
Or directly inside useEffect
function useFetch (url) {
const [data, setData] = useState(null);
useEffect(() => {
async function fetchFromAPI() {
const json = await( await fetch(url) ).json();
return json
}
setData(fetchFromAPI())
},[url]);
return data;
};
Just move your function inside the useEffect, and everything will be fine:
import { useState, useEffect } from "react";
function useFetch(url) {
const [data, setData] = useState(null);
useEffect(() => {
async function fetchFromAPI() {
const json = await (await fetch(url)).json();
setData(json);
}
fetchFromAPI();
}, [url]);
return data;
}
export { useFetch };
https://codesandbox.io/s/clever-cdn-8g0me
async function fetchFromAPI(url) {
return ( await fetch(url) ).json();
}
function useFetch (url) {
const [data, setData] = useState(null);
useEffect(() => {
fetchFromAPI(url).then(setData);
}, [url, setData, fetchFromAPI]);
return data;
};
export {
useFetch
};
You can change a little and then extract fetchFromAPI, for not being created every time useFetch calls, also it's good for single responsibility.
If you understand this code very well of course you can either turn off linting for this current line or you can add the rest setData and fetchFromAPI params. And exactly in this order. Because on re-render params comparison start from first param to last, and it's better place most changed param in first place, for not checking not changed param every time the next one is changed, so if first changed, useEffect don't need to check the others and call passed function earlier
I'm trying to migrate some of my old componentDidMount code to the new useEffect hooks and I'm having problems figuring out how to emulate the callback behavior of setState
I have an array of stuff that gets pulled from an api, I need to call a function only after the state and been loaded and then only once
Previous code:
ComponentDidMount() {
const response = await getMyArrayFromAPI
this.setState({ myArray }, () => { initializeArray() })
}
Current code:
const [myArray, setMyArray] = useState([])
useEffect(() = {
const response = await getMyArrayFromAPI
setMyArray(response)
}, [])
useEffect(() => {
// one time initialization of data
// initially gets called before myArray has value, when it should be after
// gets called every time myArray changes, instead of only once
}, [myArray])
you can set myArray in the first useEffect function, but if you want to use separate functions you can just check if it's empty
useEffect(() => {
if (!myArray.length) {
// one time initialization
}
}, [myArray])
You can use the state to drive whether or not initializeArray needs to run e.g.
const [array, setArray] = useState(null);
useEffect(() => {
getMyArrayFromAPI.then(data => setArray(data || []));
}, []);
if (array) {
// this will only ever run once as we don't set `array`
// anywhere other than `useEffect`
initializeArray();
}
Depending on what initializeArray actually does, you could run it from inside then but that's entirely up to you.
I guess you could create a custom setState hook to manage your callback
const useMyCustomStateHook = (initState, cb) => {
const [customState, updateCustomState] = useState(initState);
useEffect(() => cb(customState), [customState, cb]);
return [customState, updateCustomState];
};
So you could then have
import React, {useState,useEffect} = from 'react'
const [myArray, setMyArray] = useMyCustomStateHook([], initializeArray)
useEffect(() = {
const response = await getMyArrayFromAPI
setMyArray(response)
}, [])