3KB (GZipped) state manager for React. It is tiny but powerful
NPM
npm i remos --save
YARN
yarn add remos
https://codesandbox.io/s/reblok-demo-5ksi6t
import { blok } from "reblok";
// creating a blok with initial data
const counter = blok(0);
// using use() method to bind the blok to React component
const App = () => <h1 onClick={() => counter.data++}>{counter.use()}</h1>;
import { blok } from "reblok";
// block data can be promise object
const userProfile = blok(
fetch("https://jsonplaceholder.typicode.com/users/1").then((res) =>
res.json()
)
);
console.log(userProfile.loading); // true
const UserProfile = () => {
// if the blok is still loading, a promise object will be thrown
// and Suspense component will handle loading status
const { username } = userProfile.use();
return <div>{username}</div>;
};
const App = () => (
// blok also supports suspense and error boundary
<Suspense fallback="Loading...">
<UserProfile />
</Suspense>
);
import { blok } from "reblok";
const counter = blok(0);
counter.set((prev) => prev + 1);
import { batch } from "reblok";
batch(() => {
counter.data++;
counter.data++;
counter.data++;
});
// the counter change triggers once
Docs: https://linq2js.github.io/reblok/
Create a blok from source blok, the blok data is result of selector. Passing concurrentMode to control blok updating behavior
const counter = blok(0);
const doubledCounter = blok(counter, (x) => x * 2);
// when counter blok changed, the debouncedDoubledCounter does not update immediately, it delays update in 100ms
const debouncedDoubledCounter = blok(counter, (x) => x * 2, debounce(100));
Update blok data, if the data is the same of previous one, no change is triggered. The data can be:
Register listener to listen blok change event and return unsubscribe function
Bind the blok to React component and return the blok data
const count = counter.use();
Note: use() handles suspense and error boundary automatically. If the blok is loading, a promise object throws when component is re-rendering. If the blok has an error, the error throws when component is re-rendering.
const CountValue = () => <div>{counter.use()}</div>;
const App = () => (
<Suspense fallback="Loading...">
<CountValue />
</Suspense>
);
Bind the blok to React component and return selected slice of data. By default, blok uses strict compare function to compare selector result. If the selector result is complex object, you can use shallow compare to optimize rendering
Note: Using use() with selector does not handle suspense and error boundary.
// the component always re-render when the state changed because the selector always returns new object
// prevResult !== nextResult
const { id, username } = profile.use((x) => ({
id: x.id,
username: x.username,
}));
import { shallow } from "blok";
const { id, username } = profile.use(
(x) => ({
id: x.id,
username: x.username,
}),
// using shallow compare function to optimize re-render
shallow
);
Delay updating in X milliseconds
import { blok, debounce } from "reblok";
function updateCounter() {
counter.set((prev) => prev + 1, debounce(500));
}
updateCounter(); // counter = 0
updateCounter(); // counter = 0
updateCounter(); // counter = 0
// wait in 600ms
// counter = 1
import { blok, throttle } from "reblok";
const counter = blok(0);
function updateCounter() {
counter.set((prev) => prev + 1, throttle(500));
}
updateCounter(); // counter = 1
updateCounter(); // this updating is skipped
// wait in 600ms
updateCounter(); // counter = 2
Generated using TypeDoc