Promises are essentially a way of handling asynchronous operations, a common example of this is performing API requests in React. To work these into the React lifecycle, we can use the useState hook to store the result of the promise when it is resolved and re-render the component. This tutorial is assuming you’re familiar with using hooks in React, so if you’re unsure, check out our introduction here!

What is a Promise?

Promises allow you to perform asynchronous operations in JavaScript. To construct a Promise from scratch, you can use the Promise constructor. This takes a function which takes two parameters: “resolve”, a function to call when the operation completes, and “reject”, a function to call if the operation fails. You then have to call one of these functions when your operation completes.

JavaScript gives you two methods of handling the result of a promise, the first being .then(), a function that takes a function as a parameter, which it will pass the resolved value to.

const promiseThen = new Promise((resolve, reject) => { setTimeout(() => { resolve('Hi!'); }, 1000); }); promiseThen .then((val) => { console.log(val); }) .catch((err) => console.log(err));

Nothing else will wait for your promise to resolve, but the function you provide to .then() will run when the promise resolves.

An alternative, which is usually preferred, is the “async/await” functionality; if you “await” a promise, the code will halt until the promise is finished. Here’s the above example, rewritten to use await instead.

const promiseAwait = new Promise((resolve, reject) => { setTimeout(() => { resolve('Hi!'); }, 1000); }); const result = await promiseAwait; console.log(result);

Using Promises in React on Page Load

To use the value of a Promise in React, you can use a useEffect() hook with an empty dependency array to wait for the promise to resolve, and store the result in the value of a useState hook.

Here’s an example of using this method to get a random cat, using the CatAAS API.

function PromisesPage() { const [catUrl, setCatUrl] = useState(''); const [error, setError] = useState(false); const [state, setState] = useState(''); useEffect(() => { setState('loading'); axios .get('https://cataas.com/cat?json=true') .then((res) => { console.log(res); setState('success'); setCatUrl('https://cataas.com' + res.data.url); }) .catch((err) => { console.error('Error:', err); setState('error'); setError(err); }); }, []); if (state === 'error') return ( <h1> {error.toString()} </h1> ); return ( <div> <div> {state === 'loading' ? ( <h1>Loading...</h1> ) : ( <img src="{catUrl}" /> )} </div> </div> ); }

When the page loads, the useEffect’s function will be called. This will perform the API call using the axios library, which you can find out more about here, or here. While our API call is being made we get a loading screen; it’s a good idea to keep your user informed of what is happening. When the promise resolves, we get the cat we’ve received and store it in our state. The component re-renders, and your cat is displayed!

NB: It’s a good idea to assume something might fail with your promise and prepare for that possibility. In this case, if an error is thrown, we set a different state variable, the component re-renders and our error message is displayed.

If you prefer the await syntax for asynchronous programming, you might find a problem with the above code. Rewriting it to use await you might write something like this:

useEffect(() => { setState('loading'); try { const res = await axios.get('https://cataas.com/cat?json=true'); setCatUrl(res.data.url); setState('success'); } catch (e) { setError(e); } }, []);

But this will throw an error, since we’re trying to use await in a non-async function. We also can’t just fix this by making our useEffect’s function async by changing the signature to “async () =>”, since useEffect functions have to be synchronous.

useEffect(() => { (async () => { setState('loading'); try { const res = await axios.get('https://cataas.com/cat?json=true'); setCatUrl(res.data.url); setState('success'); } catch (e) { setError(e); } })(); }, []); useEffect(() => { const fetchCat = async () => { setState('loading'); try { const res = await axios.get('https://cataas.com/cat?json=true'); setCatUrl(res.data.url); setState('success'); } catch (e) { setError(e); } }; fetchCat(); }, []);

We can solve this with IIFE, or an Immediately-Invoked Function Expression. This is essentially just defining a function and immediately calling it, and it means we can await our promise in an asynchronous function inside the useEffect.

OnClick

With the above example, we have to refresh the page in order to get a new cat. This is a bit inconvenient, so lets refactor our site so that we can get a new cat through a button.

function PromisesPage() { const [catUrl, setCatUrl] = useState(''); const [error, setError] = useState(false); const [state, setState] = useState(''); function fetchCat() { setState('loading'); axios .get('https://cataas.com/cat?json=true') .then((res) => { console.log(res); setState('success'); setCatUrl('https://cataas.com' + res.data.url); }) .catch((err) => { console.error('Error:', err); setState('error'); setError(err); }); } useEffect(() => { fetchCat(); }, []); if (state === 'error') return ( <h1> {error.toString()} </h1> ); return ( <div> <div> {state === 'loading' ? ( <h1>Loading...</h1> ) : ( <img src="{catUrl}" /> )} </div> <button> New Cat? </button> </div> ); }

We’ve refactored the contents of our useEffect function to their own separate function, so that we can trigger it whenever we want

Now when I click the button, the onClick button triggers our API request. When our promise resolves, the value is stored in state, and this triggers a re-render.

I’ve used an API call in this example, but you could replace this with any promise, whether that’s something as simple as a useTimeout, or something more complex. Let me know in the comments below if you liked this article, if I helped solve your issue, or if you’re having any troubles!

Avatar photo
👋 Hey, I'm Omari Thompson-Edwards
Hey, I'm Omari! I'm a full-stack developer from the UK. I'm currently looking for graduate and freelance software engineering roles, so if you liked this article, reach out on Twitter at @marile0n

💬 Leave a comment

Your email address will not be published. Required fields are marked *

We will never share your email with anyone else.