React 19 Enhanced hooks
2024-05-10
ReactJS stands tall as a go-to tool in front-end development, loved for its code foundation and lively community. What really draws me to React is its dedicated team and the passionate support from its users.
The future of React is bright and full of possibilities. If I had to sum it up, I’d say it’s all about doing more with less.
In this article, I’ll talk about what’s new in React 19 so you can dive into the changes and explore its features.
But remember, React 19 is still a work in progress. Stay updated by checking the official guide on GitHub and following the team on social media.
🪝 Enhanced hooks
React Hooks have been one of the most loved features introduced in the library. You have likely used React’s built-in hooks many times, and perhaps you’ve tried making your own custom hooks, too. Hooks are so popular that they’ve become a React programming pattern.
In React 19, the way we use useMemo, forwardRef, useEffect, and useContext will change. This is mainly because a new hook, use, will be introduced.
🥁 useMemo()
You won’t need to use the useMemo() hook after React 19, as React Compiler will memoize by itself.
Before:
function ExampleComponent() {
const [inputValue, setInputValue] = useState('');
// Memoize the result of checking if the input value is empty
const isInputEmpty = useMemo(() => {
console.log('Checking if input is empty...');
return inputValue.trim() === '';
}, [inputValue]);
return (
<div>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Type something..."
/>
<p>{isInputEmpty ? 'Input is empty' : 'Input is not empty'}</p>
</div>
);
}
After:
import React, { useState } from 'react';
function ExampleComponent() {
const [inputValue, setInputValue] = useState('');
const isInputEmpty = () => {
console.log('Checking if input is empty...');
return inputValue.trim() === '';
});
return (
<div>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Type something..."
/>
<p>{isInputEmpty ? 'Input is empty' : 'Input is not empty'}</p>
</div>
);
}
🥁 forwardRef()
Ref will be now passed as props rather than using the forwardRef() hook. This will simplify the code. So after React 19, you won’t need to use forwardRef().
Before:
const ExampleButton = forwardRef((props, ref) => (
{" "}
<button ref={ref}>{props.children}</button>
)); ```
<h3>After:</h3>
```jsx import React from 'react';
const ExampleButton = ({ ref, children }) => (
{" "}
<button ref={ref}>{children}</button>
); ```
<h2>🥁 The new use() hook</h2>
<p>
React19 will introduce a new hook called use(). This hook will simplify how we
use promises, async code, and context.
</p>
<p>Here is the syntax of the hook:</p>
<pre>
<code>const value = use(resource);</code>
</pre>
<p>
The following example demonstrates how to use the use hook to make a fetch
request:
</p>
```jsx
import { use } from "react";
const fetchUsers = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
return res.json();
};
const UsersItems = () => {
const users = use(fetchUsers());
return (
<ul>
{users.map((user) => (
<div key={user.id} className="bg-blue-50 shadow-md p-4 my-6 rounded-lg">
<h2 className="text-xl font-bold">{user.name}</h2>
<p>{user.email}</p>
</div>
))}
</ul>
);
};
export default UsersItems;
Lets explain this code:
fetchUsers()
handles the GET request.
We utilize the use()
hook to execute fetchUsers()
instead of employing the useEffect or useState hooks.
The return value of the useState hook, users
, contains the
response of the GET request (users).
In the return block, users
is mapped over to create the list.
Now, instead of useContext()
, we will have
use(context)
for managing global states with the Context API
const CustomThemeContext = createContext();
const CustomThemeProvider = ({ children }) => {
const [currentTheme, setCurrentTheme] = useState('light');
const changeTheme = () => {
setCurrentTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<CustomThemeContext.Provider value={{ currentTheme, changeTheme }}>
{children}
</CustomThemeContext.Provider>
);
};
const CustomCard = () => {
// Custom use Hook()
const { currentTheme, changeTheme } = use(CustomThemeContext);
return (
<div
className={`p-4 rounded-md ${
currentTheme === 'light' ? 'bg-white' : 'bg-gray-800'
}`} >
<h1
className={`my-4 text-xl ${
currentTheme === 'light' ? 'text-gray-800' : 'text-white'
}`} >
Theme Card
</h1>
<p className={currentTheme === 'light' ? 'text-gray-800' : 'text-white'}>
Hello!! use() hook
</p>
<button
onClick={changeTheme}
className='bg-blue-500 hover:bg-blue-600 text-white rounded-md mt-4 p-4'
>
{currentTheme === 'light' ? 'Switch to Dark Mode' : 'Switch to Light Mode'}
</button>
</div>
);
};
const CustomTheme = () => {
return (
<CustomThemeProvider>
<CustomCard />
</CustomThemeProvider>
);
};
export default CustomTheme;
🥁 The useFormStatus() Hook
This new hook in React19 provides enhanced control over the forms you create, offering status information about the last form submission.
Here’s the syntax:
const { isPending, formData, method, action } = useFormStatus();
or the simplified version:
jsx const {status} = useFormStatus()
Let’s delve into its functionality:
isPending: Indicates if the form is currently pending submission; true if pending, false otherwise.
formData: An object implementing the FormData interface containing the data the parent <form> is submitting.
method: The HTTP method – GET or POST. Defaults to GET.
action: A reference to a function.
This hook facilitates displaying a pending state and the submitted data from the user.
Here’s a code example:
import { useFormStatus } from "react-dom";
function SubmitButton() {
const { isPending } = useFormStatus();
return <button disabled={isPending}>{isPending ? 'Submitting...' : 'Submit'}</button>;
}
const formSubmitAction = async () => {
// Simulate a delay of 2 seconds
await new Promise((resolve) => setTimeout(resolve, 3000));
}
const FormStatus = () => {
return (
<form action={formSubmitAction}>
<SubmitButton />
</form>
);
};
export default FormStatus;
Here’s the breakdown of the code:
- SubmitButton is a component responsible for the form submission action. It retrieves the submission status using
useFormStatus()
and disables the button accordingly. - Based on the isPending status, the appropriate message is displayed in the UI.
- formSubmitAction is a simulated function to delay the form submission.
🥁 The useCustomFormState() Hook
Another new hook in React19 is useCustomFormState. It allows you to update state based on the result of a form submission.
Here’s the syntax:
const [customState, customFormAction] = useCustomFormState(submitHandler, initialState, permalink?);
submitHandler: the function to be called when the form is submitted or button is pressed.
initialState: the value you want the state to be initially. It can be any serializable value. This argument is ignored after the action is first invoked.
permalink: this is optional. A URL or page link, if submitHandler is going to be run on server then the page will redirect to permalink.
This hook will return:
customState: the initial state will be the value we have passed to initialState.
customFormAction: an action that will be passed to the form action. The return value of this will be available in the state.
Here’s an example of how it works:
import { useCustomFormState } from "react-dom";
const CustomFormState = () => {
const customSubmitHandler = (prevState, formData) => {
const name = formData.get("username");
console.log(prevState); // previous form state
if (name === "john") {
return {
success: true,
text: "Welcome",
};
} else {
return {
success: false,
text: "Error",
};
}
};
const [message, customFormAction] = useCustomFormState(
customSubmitHandler,
null
);
return (
<form action={customFormAction}>
<label>Name</label>
<input type="text" name="username" />
<button>Submit</button>
{message && <h1>{message.text}</h1>}
</form>
);
};
export default CustomFormState;
Here’s the breakdown of the code:
- customSubmitHandler is the method responsible for the form submission. This is the Action (remember Action new React19 feature).
- Inside customSubmitHandler, we are checking the value of the form. Then, depending on whether it’s successful or shows an error, we return the specific value and message. In the above code example, if there is any value other than “John”, then it will return an error.
- We can also check the prevState of the form. The initial state would be null, and after that, it will return the prevState of the form.
On running this example, you will see a “welcome” message if the name is John – otherwise, it will return “error”.
🥁 The useCustomOptimistic() Hook
useCustomOptimistic is a React Hook that lets you show a different state while an async action is underway, according to the React docs.
This hook will help enhance the user experience and should result in faster responses. This will be useful for applications that need to interact with the server.
Here’s the syntax of the useCustomOptimistic hook:
const [customOptimisticState, customAddOptimisticState] = useCustomOptimistic(state, updateFn)
For example, while a response is on the way, we can show a “state” to give the user an immediate response. Once the actual response is returned from the server, the “optimistic” state will be replaced by it.
The useCustomOptimistic hook will immediately update the UI assuming the request will succeed. The name is “optimistic” because the user will see the optimistic (success) result of performing an action, even though the action actually takes time to complete.
Let’s look at how we can implement the useCustomOptimistic hook. The below code shows the optimistic state on click of the button submit <form input> (Sending…) until the response doesn’t come.
import { useCustomOptimistic, useState } from "react";
const CustomOptimistic = () => {
const [messages, setMessages] = useState([
{ text: "Hey, I am initial!", sending: false, key: 1 },
]);
const [optimisticMessages, addCustomOptimisticMessage] = useCustomOptimistic(
messages,
(state, newMessage) => [
...state,
{
text: newMessage,
sending: true,
},
]
);
async function sendFormData(formData) {
const sentMessage = await fakeDelayAction(formData.get("message"));
setMessages((messages) => [...messages, { text: sentMessage }]);
}
async function fakeDelayAction(message) {
await new Promise((res) => setTimeout(res, 1000));
return message;
}
const submitData = async (userData) => {
addCustomOptimisticMessage(userData.get("username"));
await sendFormData(userData);
};
return (
<>
{optimisticMessages.map((message, index) => (
<div key={index}>
{message.text}
{!!message.sending && <small> (Sending...)</small>}
</div>
))}
<form action={submitData}>
<h1>CustomOptimisticState Hook</h1>
<div>
<label>Username</label>
<input type="text" name="username" />
</div>
<button type="submit">Submit</button>
</form>
</>
);
};
export default CustomOptimistic;
Here’s the breakdown of the code:
- fakeDelayAction is a fake method to delay the submit event. This is to show the optimistic state.
- submitData is the action. This method is responsible for the form submission. This could be async, too.
- sendFormData is responsible for sending the form to fakeDelayAction
- Setting the default state. messages will be used in the useCustomOptimistic() as input and will return in optimisticMessages.
❓Can I Try This Hooks Now?
Currently, all the exciting features discussed above are accessible in the canary release. You can delve deeper into these enhancements here. As advised by the React team, it’s not recommended to integrate these features into production-ready customer or user-facing applications yet. However, feel free to experiment and explore for your own educational purposes or simply for fun.
If you’re curious about the official release date of React 19, keep an eye on the Canary Releases for any updates.
To stay informed about the latest developments, you can follow the React team on various platforms: