As people say: “It’s only stupid if it doesn’t work”.
The basics you might not know
First thing, initializing
It’s convention to initialize a state as:
const [state, setState] = useState();
But… useState
is just a function that returns an array and you can dump it anywhere you want as long as you don’t violate the rule of hooks.
And while we are at it, you can initialize it with a function, that function will run only once no matter what and that’s it.
Second thing, what can be a state
ANYTHING! (as far as I know =p)
What about a promise
then? Yes, you can have promises in a state, of course, that you can’t unpack them inside the JSX part.
First version
With that in mind, you’re probably thinking something along this lines:
function FirstVersion() {
const [state, setState] = useState(() =>
fakeFetch("First Version!")
.then((val) => setState(val))
.catch((err) => setState(err))
);
return (
<div style={{ marginTop: "2em" }}>
{
(state instanceof Promise)
? "Loading First Version Component..."
: state
}
</div>
);
}
And that will work!
useStatePromise
So, I’ve played around and made this custom hook:
import { useEffect, useState } from "react";
export function useStatePromise(promise) {
const [error, setError] = useState(null);
const [value, setValue] = useState(() => {
promise
.then((val) => setValue(val))
.catch(setError);
});
const [newValue, setNewValue] = useState(null);
useEffect(() => {
if (newValue instanceof Promise) {
newValue.then(setValue).catch(setError);
} else {
setValue(newValue);
}
}, [newValue, setValue]);
return [value, setNewValue, error];
}
Adding to what I did in the first version, first I extracted that into a custom hook, then I added a way to be able to change the state, this is done by using another state to keep the promise until finished.
Example project
Bonus - useRefPromise
Since I was there, I also played around the useRef
import { useRef } from "react";
export function useRefPromise(promise) {
const ref = useRef(promise.then((val) => (ref.current = val)));
if (ref.current instanceof Promise) {
return null;
}
return ref.current;
}
If you’ve opened the example, you will see it works… but be careful!
It only works because the useEffect
keeps forcing renders that will make it pick up the ref latest values. So… in a few scenarios, it would work.
Comment out the useEffect
with setInterval
and see how it would behave then.
Outro
React functions are just that, functions. Yes, there are a lot of things happening in the background, but as far as you’re concerned, it’s functions and so you can do some crazy stuff with it.
Then again, all of these are probably some bad ideas that you shouldn’t really use unless you actually have a case that would, someway somehow, benefit from it.
Cover Photo by Womanizer Toys on Unsplash