rerecoil
v0.1.0
Published
React state management library. A successor to Recoil.
Maintainers
Readme
Rerecoil
React state management library. A successor to Recoil.
Read this in Korean.
Installation
npm install rerecoil
# or
yarn add rerecoil
# or
pnpm add rerecoilBasic Usage
Using Atoms
Atoms are units of state.
import { atom, useRecoilState } from "rerecoil";
// Define an atom
const counterState = atom({
key: "counterState",
default: 0,
});
function Counter() {
const [count, setCount] = useRecoilState(counterState);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
</div>
);
}Using Selectors
Selectors represent derived state.
import { atom, selector, useRecoilValue } from "rerecoil";
const counterState = atom({
key: "counterState",
default: 0,
});
// Read-only selector
const doubledCountState = selector({
key: "doubledCountState",
get: ({ get }) => {
const count = get(counterState);
return count * 2;
},
});
function DoubledCounter() {
const count = useRecoilValue(counterState);
const doubledCount = useRecoilValue(doubledCountState);
return (
<div>
<p>Count: {count}</p>
<p>Doubled Count: {doubledCount}</p>
</div>
);
}Writable Selectors
import { atom, selector, useRecoilState } from "rerecoil";
const tempFahrenheit = atom({
key: "tempFahrenheit",
default: 32,
});
// Writable selector
const tempCelsius = selector({
key: "tempCelsius",
get: ({ get }) => {
const fahrenheit = get(tempFahrenheit);
return ((fahrenheit - 32) * 5) / 9;
},
set: ({ set }, newCelsiusValue) => {
const newFahrenheit = (newCelsiusValue * 9) / 5 + 32;
set(tempFahrenheit, newFahrenheit);
},
});
function TempConverter() {
const [fahrenheit, setFahrenheit] = useRecoilState(tempFahrenheit);
const [celsius, setCelsius] = useRecoilState(tempCelsius);
return (
<div>
<input
value={fahrenheit}
onChange={(e) => setFahrenheit(Number(e.target.value))}
placeholder="Fahrenheit (°F)"
/>
<input
value={celsius}
onChange={(e) => setCelsius(Number(e.target.value))}
placeholder="Celsius (°C)"
/>
</div>
);
}Advanced Features
AtomFamily & SelectorFamily
AtomFamily and SelectorFamily create collections of atoms or selectors that share similar configuration but have different parameters.
import {
atomFamily,
useRecoilState,
selectorFamily,
useRecoilValue,
} from "rerecoil";
// Define an atom family
const userState = atomFamily({
key: "user",
default: (userId) => ({
id: userId,
name: "Loading...",
email: "",
}),
});
// Define a selector family that depends on the atom family
const userNameState = selectorFamily({
key: "userName",
get:
(userId) =>
({ get }) => {
const user = get(userState(userId));
return user.name;
},
});
function UserProfile({ userId }) {
const [user, setUser] = useRecoilState(userState(userId));
const userName = useRecoilValue(userNameState(userId));
return (
<div>
<h2>{userName}</h2>
<p>Email: {user.email}</p>
<button onClick={() => setUser({ ...user, name: "Updated Name" })}>
Update Name
</button>
</div>
);
}Async Selectors
Selectors can handle asynchronous data by returning a Promise.
import { selector, useRecoilValueLoadable } from "rerecoil";
const userDataState = selector({
key: "userData",
get: async () => {
const response = await fetch("https://api.example.com/user");
return response.json();
},
});
function UserData() {
const userDataLoadable = useRecoilValueLoadable(userDataState);
return (
<div>
{userDataLoadable.state === "loading" && <p>Loading...</p>}
{userDataLoadable.state === "hasError" && (
<p>Error: {userDataLoadable.error.message}</p>
)}
{userDataLoadable.state === "hasValue" && (
<div>
<h2>{userDataLoadable.contents.name}</h2>
<p>Email: {userDataLoadable.contents.email}</p>
</div>
)}
</div>
);
}Snapshot API
The Snapshot API allows you to capture and manage the state of your Recoil atoms at a specific point in time.
import {
atom,
useRecoilState,
useRecoilSnapshot,
snapshot_UNSTABLE,
useRecoilCallback,
} from "rerecoil";
// Create a snapshot of the current state
const mySnapshot = snapshot_UNSTABLE();
// Get a value from the snapshot
const value = mySnapshot.getLoadable(myAtom).contents;
// Use the snapshot in a component
function SnapshotExample() {
const [count, setCount] = useRecoilState(counterState);
const snapshot = useRecoilSnapshot();
const logState = useRecoilCallback(({ snapshot }) => async () => {
const currentCount = await snapshot.getPromise(counterState);
console.log("Current count:", currentCount);
});
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={logState}>Log State</button>
</div>
);
}Utility Functions
Rerecoil provides utility functions for handling multiple asynchronous selectors.
import {
selector,
waitForAll,
waitForAny,
noWait,
useRecoilValue,
} from "rerecoil";
// Wait for all selectors to resolve
const combinedDataState = selector({
key: "combinedData",
get: ({ get }) => {
const { users, posts, comments } = get(
waitForAll({
users: usersState,
posts: postsState,
comments: commentsState,
})
);
return { users, posts, comments };
},
});
// Wait for any selector to resolve
const fastestDataState = selector({
key: "fastestData",
get: ({ get }) => {
return get(waitForAny([usersState, postsState, commentsState]));
},
});
// Get immediate state without waiting
function NoWaitExample() {
const userDataLoadable = useRecoilValue(noWait(userDataState));
if (userDataLoadable.state === "loading") {
return <div>Loading user data...</div>;
}
return <div>User state: {userDataLoadable.state}</div>;
}Roadmap
Check out our TODO list (한국어) for planned features and improvements.
Contributing
We welcome contributions! Please see our contributing guidelines (한국어) for more information.
License
MIT
