Unlocking the Power of Recoil: A Step-by-Step Guide to Refactoring Redux Apps
Learning by Refactoring: A Proven Strategy
One effective way to learn is by refactoring existing codebases from known to unknown technologies. In this case, we’ll refactor three small Redux apps to Recoil, demonstrating the simplicity and efficiency of Recoil’s state management system.
HelloUI: The Hello World Application for UI Development
Let’s start with a simple “hello world” app, bootstrapped with create-react-app and using React bindings for Redux via react-redux. To refactor this app to Recoil, we’ll begin by wrapping our root component in a “provider” component, similar to react-redux.
import { RecoilRoot } from 'ecoil';
function App() {
return (
<RecoilRoot>
<HelloWorld />
</RecoilRoot>
);
}
Unlike react-redux, Recoil doesn’t require a store prop; instead, it uses a Context Provider to manage state values internally.
Refactoring the Global State
Next, we’ll create and update the global state using Recoil’s atom function, which represents a piece of state.
import { atom } from 'ecoil';
const greetingState = atom({
key: 'greetingState',
default: 'Hello, World!',
});
We’ll then use the useRecoilState Hook to read and update the state value, similar to React’s useState Hook.
import { useRecoilState } from 'ecoil';
function HelloWorld() {
const [greeting, setGreeting] = useRecoilState(greetingState);
return (
<div>
<p>{greeting}</p>
<button onClick={() => setGreeting('Hello, Universe!')}>
Update Greeting
</button>
</div>
);
}
Redux App #2: Fetching Data with Thunks
In our second example, we’ll refactor an app that fetches user profile details from a remote server using Redux-thunk.
import { atom, selector } from 'ecoil';
const userDataState = atom({
key: 'userDataState',
default: null,
});
const fetchUserData = selector({
key: 'fetchUserData',
get: async () => {
const response = await fetch('/api/user');
return response.json();
},
});
With Recoil, we’ll create an atom to hold the fetched data and use the useRecoilState Hook to read and update the state value. We’ll also trigger the data fetch using an async function, which sets the global state using the state updater function.
import { useRecoilState, useRecoilValueLoadable } from 'ecoil';
function UserProfile() {
const [userData, setUserData] = useRecoilState(userDataState);
const userDataLoadable = useRecoilValueLoadable(fetchUserData);
if (userDataLoadable.state === 'loading') {
return <p>Loading...</p>;
}
if (userDataLoadable.state === 'hasError') {
return <p>Error: {userDataLoadable.contents}</p>;
}
return <p>User Profile: {userData.name}</p>;
}
Redux App #3: Multiple User Profiles Fetched with Thunks
Our final example showcases Recoil’s strength in handling complex state dependencies.
import { atom, selector } from 'ecoil';
const currentUserIdState = atom({
key: 'currentUserIdState',
default: 1,
});
const userSelector = selector({
key: 'userSelector',
get: async ({ get }) => {
const currentUserId = get(currentUserIdState);
const response = await fetch(`/api/users/${currentUserId}`);
return response.json();
},
});
With Recoil, we’ll create an atom to hold the current user ID and an async selector that fetches the user data based on the ID. Recoil will efficiently recompute the state values and re-render components subscribed to these values.
The Benefits of Recoil
Recoil’s data-flow graph and efficient state management system make it an attractive alternative to Redux. By focusing on the flow of state via a graph data structure, Recoil provides a more intuitive and simple API, making it easier to manage complex state dependencies.
- Efficient state management: Recoil’s data-flow graph ensures that only the necessary components are re-rendered when the state changes.
- Simple API: Recoil’s API is designed to be easy to learn and use, making it accessible to developers of all skill levels.
- Handling complex state dependencies: Recoil’s async selectors and atoms make it easy to manage complex state dependencies, such as fetching data from multiple sources.
Get started with Recoil today and explore its capabilities!