react useEffect with async-await not working as expected - reactjs

I have a useEffect hooks in my component, which makes a API call and I want it to run only on first render. But I'm unable to make the API call. what am I missing here?
useEffect(() => {
//should run on first render
(async () => {
const getAllSeasons = await getSeasonList();
setSeasons(getAllSeasons);
})();
}, []);
const getSeasonList = async () => {
if (state && state?.seasonList) {
return state?.seasonList;
} else {
const seasonData = await useSeasonService();
if (seasonData?.status === "loaded") {
return seasonData?.payload?.seasons || [];
} else if (seasonData.status == "error") {
return [];
}
}
};

Best way to fetch APIs using fetch method
fetch('https://apisaddress.me/api/')
.then(({ results }) => consoe.log(results) ));
Another aproach is using axios
const Func= async ()=>{
const response= await axios.get('https://pokeapi.co/api/v2/pokemon?
limit=500&offset=200')
this.setState({Data:response.data.results})
}
// npm install axios

Make the new function and clean all code from useEffect and put inside that function. and then call the function inside the useEffect. Like:
const sampleCall = async () => {
const getAllSeasons = await getSeasonList();
setSeasons(getAllSeasons);
}
useEffect(() => {
sampleCall()
}, [])
Follow these steps, if it is still not working then try to add seasons inside the useEffect array, [seasons].
Thank You.

useEffect works fine. The proof is here https://codesandbox.io/s/set-seasons-9e5cvn?file=/src/App.js.
So, the problem is in getSeasonList function.
await useSeasonService() won't work. React Hooks names start with use word and can't be called inside functions or conditionally. useSeasonService is considered by React engine as a custom hook. Chek this for more details:
https://reactjs.org/docs/hooks-custom.html#extracting-a-custom-hook
Your code example doesn't show what state is and how it's initialized.
state && state?.seasonList check is redundant. state?.seasonList is enough.
It's a bad practice to put such complex logic as in getSeasonList into React components. You'd better use some state container (Recoil might be a good choice if you have some small non-related states in the app).
P.S. You wrote a poor description of the problem and then gave minuses for answers. That's not fair. Nobody would help you if you'll continue to do such thing. You gave 4 minuses and received 4 minuses in response. It's better to clarify what's wrong before giving a plus or minus to any answer.

Related

How do i put a setstate function that is already within an async function, within a useEffect hook?

I am working on a project, which is a django project with REACT as the frontend. For the homepage, there is a useState variable ('room_code') that is used. The setstate variable is set_room_code. So, i have an async function that fetches the room code from an api and then the idea is to use the set_room_code hook. But this is just not working. The issue is with the set_room_code as the code works if i simply remove it. I have tried to search up ideas but i am short on it. Any input would be appreciated.
useEffect( () => {
let fetch_code = async () => {
const response = await fetch('/api/user-room');
const data = await response.json();
console.log('hi');
console.log(data.room_code);
console.log('bhao');
set_room_code(data.room_code);
};
fetch_code();
console.log(hi);
}, []);
I have tried using an extra useEffect hook but that doesnt work as well
A few things, first its best practice to name the useState variable
const [roomCode, setRoomCode] = useState();
FYI.
Now as to your question--
useEffect( () => {
let fetch_code = () => {
fetch('/api/user-room').then((data) => {
setRoomCode(data.room_code);
console.log(data.room_code);
return response.json();
});
};
fetch_code();
console.log("this should show your RoomCode", roomCode)
}, [roomCode]);

Update state in setInterval via dispatch outside component

I currently have a functional component Form that triggers a task to occur. Once the submission is complete, I create a setInterval poll to poll for the status of the task. The code roughly looks like
export function Form(props: FormProps) {
const dispatch = useDispatch()
const pollTaskStatus = () => {
const intervalId = setInterval(async() => {
const response = await fetchTaskStatus() // Function in different file
if (response.status === 'COMPLETE') {
dispatch(Actions.displayTaskComplete())
clearInterval(intervalId)
}
})
}
const submitForm = async() => {
await onSubmitForm() // Function in different file
pollTaskStatus()
}
return (
...
<button onClick={submitForm}>Submit</button>
)
}
When the action is dispatched, the redux store is supposed to be updated and a component is supposed to update alongside it showing a message that the task is complete. However, I see the action logged with an updated store state but nothing occurs. If I just try to dispatch the same action with useEffect() wrapped around it outside the submitForm functions, the message appears. I've searched online and people say that you need to wrap useEffect around setInterval but I can't do that because the function that calls setInterval is not a custom hook or component. Is there a way to do this?
It's a bit difficult to answer your question without seeing all the code.
But my guts feeling is that this might no have anything to do with React.
const pollTaskStatus = () => {
const intervalId = setInterval(async() => {
console.log('fire to fetch')
const response = await fetchTaskStatus() // Function in different file
if (response.status === 'COMPLETE') {
console.log('success from fetch')
dispatch(Actions.displayTaskComplete())
}
})
}
Let's add two console lines to your code. What we want to see is to answer the following questions.
is the setInterval called in every 500ms?
is any of the call finished as success?
how many dispatch has been fired after submission
If you can answer all these questions, most likely you can figure out what went wrong.

how to write a clean up function in react?

I keep trying to understand how to fix memory leaks in useEffect() but still am having trouble understanding.
The error code says in order to fix the leak I must cancel all subscriptions and asynchronous calls with a clean up function.
This is how my code looks like at the moment.
const [dish,setDish] = useState({title:""})
useEffect(()=>{
fetchDish()
},[dish.title])
const fetchDish = async() => {
const apiCall = await fetch('https://exampleurl.json')
const dishes = await apiCall.json()
setDish(dishes[props.dishID] )
}
I am assuming i'm getting a memory leak because I have to destroy my dish object once this unmounts?
You need to use componentWillUnmount technique here, as this is a functional component returning from useEffect will cause the same behavior.
For Example:
useEffect(() => {
fetchDish()
return () => {
//unmounting code here..
//in your case
setDish({})
}
}, [dish.title])
First, I think you should read this article by Dan Abramov to get a better understanding of useEffect
If you only use the function in your useEffect, then move it inside the useEffect so it doesn't affect the data flow. If you want to re-use the fetchDish, then use useCallback so it doesn't get called if you don't need it.
If your case is about cleaning the code, then Rishav's answer is correct
useEffect( () => {
subscribe();
return () => unsubscribe();
}
However I think what react is trying to tell you is because of your asyncrhonous call that keep getting called
Try to modify your code to this
const [dish,setDish] = useState({title:""})
useEffect(()=>{
async function fetchDish(){
const apiCall = await fetch('https://exampleurl.json')
const dishes = await apiCall.json()
setDish(dishes[props.dishID] )
}
fetchDish()
},[])

React Hooks: Referencing data that is stored inside context from inside useEffect()

I have a large JSON blob stored inside my Context that I can then make references to using jsonpath (https://www.npmjs.com/package/jsonpath)
How would I go about being able to access the context from inside useEffect() without having to add my context variable as a dependency (the context is updated at other places in the application)?
export default function JsonRpc({ task, dispatch }) {
const { data } = useContext(DataContext);
const [fetchData, setFetchData] = useState(null);
useEffect(() => {
task.keys.forEach(key => {
let val = jp.query(data, key.key)[0];
jp.value(task.payload, key.result_key, val);
});
let newPayload = {
jsonrpc: "2.0",
method: "call",
params: task.payload,
id: "1"
};
const domain = process.env.REACT_APP_WF_SERVER;
let params = {};
if (task.method === "GET") {
params = newPayload;
}
const domain_params =
JSON.parse(localStorage.getItem("domain_params")) || [];
domain_params.forEach(e => {
if (e.domain === domain) {
params[e.param] = e.value;
}
});
setFetchData({ ...task, payload: newPayload, params: params });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [task]);
}
I'm gonna need to post an answer because of code, but I'm not 100% sure about what you need, so I'll build a correct answer with your feedback :)
So, my first idea is: can't you split your effects in two React.useEffect? Something like this:
export default function JsonRpc({ task, dispatch }) {
...
useEffect(() => {
...
setFetchData(...);
}, [task]);
useEffect(() => {
...
}, [data]);
..
}
Now, if my understanding are correct, this is an example of events timeline:
Due to the update on task you will trigger the first useEffect, which can setFetchData();
Due to the update on fetchData, and AXIOS call is made, which updates data (property in the context);
At this, you enter the second useEffect, where you have the updated data, but NO call to setFetchData(), thus no loop;
Then, if you wanted (but couldn't) put data in the dependencies array of your useEffect, I can imagine the two useEffect I wrote have some shared code: you can write a common method called by both useEffects, BUT it's important that the setFetchData() call is outside this common method.
Let me know if you need more elaboration.
thanks for your reply #Jolly! I found a work around:
I moved the data lookup to a state initial calculation:
const [fetchData] = useState(processFetchData(task, data));
then im just making sure i clear the component after the axios call has been made by executing a complete function passed to the component from its parent.
This works for now, but if you have any other suggestions id love to hear them!

I cannot collect data from API using Axios + React

I'm beginner with React. I have 2 different cases where I'm using React Hooks which I cannot receive the data from my local API properly.
Case 1:
export const RegisterT = () => {
const [test, setTest] = useState()
const addrState = {}
axios.get('http://127.0.0.1:3333/states', { addrState })
.then(res => {
setTest(res.data)
console.log(test)
})
...
}
It works with the state test displaying correctly the content from the API but I don't know why/how the Axios continues calling the API infinity - endless. (Ps: the very first call it returns undefined, then the next ones it works) What am I doing wrong?
To fix this I've tried to use useEffect like this (Case 2):
export const RegisterT = () => {
const [test, setTest] = useState()
const addrState = {}
useEffect(() => {
axios.get('http://127.0.0.1:3333/states', { addrState })
.then(res => {
setTest(res.data)
console.log(test);
})
}, [])
...
}
Now the Axios works only once but no data is coming from the API. Maybe I should use async/wait for this case but I cannot make it work. Does anyone know how to fix that (Case 1 or/and Case 2)?
Thanks.
Updating the state is an asynchronous operation. So the state is not really updated until the next time the component gets rendered. If you want to capture the correct state, you can either console.log(res.data) or wrap that inside the useEffect hook with test as dependency.
export const RegisterT = () => {
const [test, setTest] = useState()
const addrState = {}
// effect only runs when component is mounted
useEffect(() => {
axios.get('http://127.0.0.1:3333/states', { addrState })
.then(res => {
setTest(res.data);
});
}, []);
// effect runs whenever value of test changes
useEffect(() => {
console.log(test);
}, [test]);
}
That way it is guaranteed that the console.log runs when the value of test is updated.
Also the reason the API request is invoked once is you have not mentioned anything in the dependency array. [] empty dependency array runs the effect when the component is mounted for the first time.
async/await is just a wrapper around Promise object. So they would behave similarly.
The solution with useEffect is good. If you don't use it each render will call the request. This is the same if you put there console.log with any information. The reason why you don't see the data in the useEffect is that the value of the state is not updated in current render but in the next which is called by setter of the state. Move the console.log(test); after useEffect to see the data. On init it will be undefined but in the next render, it should contain the data from the request.

Resources