wait for state to change in the same function? - reactjs

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 );
}

Related

Should I call a function that returns a promise immediately after setState or in the dependency array of useEffect()

My code (which seems to work ok) looks like this:
import { SplashScreen } from "#capacitor/splash-screen";
const MyComponent = () => {
const [data, setData] = useState()
useEffect(() => {
init()
}, [])
const init = async () => {
const response = await fetch("some_api")
setData(response.data)
await SplashScreen.hide()
}
return (<div>{JSON.stringify(data)}<div>)
}
But I'm wondering if it's better practive to move the await SplashScreen.hide() function call to a useEffect() with the data in the dependency array like this:
import { SplashScreen } from "#capacitor/splash-screen";
const MyComponent = () => {
const [data, setData] = useState()
useEffect(() => {
init()
}, [])
useEffect(() => {
if (data) {
await SplashScreen.hide()
}
}, [data])
const init = async () => {
const response = await fetch("some_api")
setData(response.data)
}
return (<div>{JSON.stringify(data)}<div>)
}
Note: SplashScreen.hide() returns a promise. Which way is better and why?
It depends if you want to call SplashScreen.hide() after the data has been set or not. In the first case it's not guaranteed that the call will be made after the data is set since setState works async. In the second case though, you are explicitly calling SplashScreen.hide() after the state has been updated.
Note, since you're not doing anything with the promise returned from SplashScreen.hide(), it's not necessary to call it with await.

How to trigger callback using useState synchronously if callback was external

I have the following hook:
const MessageStorage = () => {
const [messages, setMessages] = useState([]);
const addMessage = (message) => { ... }
const reset = (callback) => {
setMessages([])
callback(); // wrong since setMessages([]) is asynchronous
}
return { addMessage, reset };
}
The problem is with reset function. The standard approach to triggering callback is to use useEffect to track the change of state. But the problem is that callback is defined by the consumer of MessageStorage hook, and it must be defined as a parameter of reset function.
What's the best way to tackle this?
One approach is to put the callback into state:
const [callback, setCallback] = useState();
// ...
const reset = (callback) => {
setMessages([])
setCallback(callback);
}
useEffect(() => {
callback?.();
setCallback(); // make sure callback only gets called once
}, [messages]);

React infinity loop when making HTTP calls using useEffect

I am trying to make 2 HTTTP calls inside a React component that will then call the setters for 2 properties that are defined using useState. I have followed what I thought was the correct way of doing so in order to prevent inifinite rerendering but this is still happening. Here is my code:
function Dashboard({ history = [] }) {
const [teamInfo, setTeamInfo] = useState(null);
const [survey, setSurvey] = useState(null);
const [open, setOpen] = useState(false);
const user = getUser();
const getSurveyHandler = async () => {
const surveyResponse = await getSurveys('standard');
setSurvey(surveyResponse.data);
};
const getTeamInfoHandler = async () => {
const teamInfoResponse = await getTeamInfo(user.teamId);
setTeamInfo(teamInfoResponse);
};
useEffect(() => {
document.body.style.backgroundColor = '#f9fafb';
getSurveyHandler();
getTeamInfoHandler();
}, [survey, teamInfo]);
As you can see, I have defined the functions outside of the useEffect and passed in the two state variables into the dependency array that will be checked to prevent infinite rerendering.
Can anyone see why this is still happening?
Thanks
You are setting survey and teamInfo in your functions with a dependency on them in your useEffect.
useEffect runs everytime a dependency changes. You are setting them, causing a rerender. Since they changed, the useEffect runs again, setting them again. The cycle continues.
You need to remove those.
useEffect(() => {
document.body.style.backgroundColor = '#f9fafb';
getSurveyHandler();
getTeamInfoHandler();
}, []);
The only other thing recommended is to move async functions inside the useEffect unless you need to call them from other places in the component.
useEffect(() => {
const getSurveyHandler = async () => {
const surveyResponse = await getSurveys('standard');
setSurvey(surveyResponse.data);
};
const getTeamInfoHandler = async () => {
const teamInfoResponse = await getTeamInfo(user.teamId);
setTeamInfo(teamInfoResponse);
};
document.body.style.backgroundColor = '#f9fafb';
getSurveyHandler();
getTeamInfoHandler();
}, []);

unable to define an ID when using useEffect

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!

Correct dependency array for useEffect with React hooks

I am using Create-React-App and the (excellent) use-http for a custom useFetch hook. The goal is to make several API calls upon login to an account area:
const [user, setUser] = useState(null)
const [profile, setProfile] = useState(null)
const [posts, setPosts] = useState(null)
const request = useFetch('/')
const initializeAccount = async () => {
try {
const user = await request.get('api/user/')
const profile = await request.get('api/profile/')
const posts = await request.get('api/posts/')
if (user) {
setUser(user.data)
}
if (profile) {
setProfile(profile.data)
}
if (posts) {
setPosts(posts.data)
}
} catch (e) {
console.log('could not initialize account')
}
}
useEffect(() => {
initializeAccount()
return () => console.log('unmount')
})
I have tried using [] as the dependency array, but I get a linting error saying to move initializeAccount to the dependency array. If I add it, the function runs endlessly.
What is the correct way to setup the dependency array so that this function is called one time? Also, what would be the correct way to handle abort of each of the API calls in this scenario?
My man, in order to run useEffect once for api calls, you have to do it like this:
useEffect(() => {
initializeAccount()
return () => console.log('unmount')
},[])
Hope it helps.

Resources