
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
fluent-state
Advanced tools
Fluent, immutable, deeply reactive state for React — powered by Proxies and fine-grained tracking
Fluent, immutable React local state that just makes sense.
Fluent, immutable React local state that just makes sense.
A tiny (~2.4kb), proxy-based React hook for deeply nested, reactive state, computed reactive state and built-in effects — zero boilerplate, no reducers, no magic.
npm install fluent-state
Or with yarn:
yarn add fluent-state
import { useFluentState } from "fluent-state";
function Counter() {
const [state, effect] = useFluentState({ count: 0 });
effect(() => {
console.log("Count changed:", state.count());
});
return (
<>
<p>Count: {state.count()}</p>
<button onClick={() => state.count((c) => c + 1)}>Increment</button>
</>
);
}
⚡ Live demo on CodeSandbox (state & effect) »
⚡ Live demo on StackBlitz (state, compute & effect) »
I built fluent-state because I wanted a React state hook that:
useState, useReducer, and useEffect with a single, fluent, and reactive APIstate.user.name("Alice"))compute for derived valuesfluent-state uses JavaScript Proxies — but not to wrap your entire state object directly.
Instead, it wraps tiny getter/setter functions that correspond to specific paths inside your state. These proxies:
This means:
state.user.name("Alice")In short: fluent-state’s proxies wrap functions representing paths, not the state object itself — keeping everything simple, predictable, and reactive.
computeBesides effect for side-effects, fluent-state also provides the compute function to create derived, reactive state that automatically updates whenever its underlying dependencies change.
import { useFluentState } from "fluent-state";
type Contact = {
id: number;
name: string;
email: string;
};
type AppState = {
form: {
name: string;
email: string;
};
contacts: Contact[];
};
export default function () {
const [state, effect, compute] = useFluentState<AppState>({
form: { name: "", email: "" },
contacts: [
{ id: 1, name: "Alice", email: "alice@example.com" },
{ id: 2, name: "Bob", email: "bob@example.com" },
],
});
effect(() => {
const contacts = state.contacts();
console.log("Contacts changed:", contacts);
});
const isFormValid = compute(() => {
const name: string = state.form.name();
const email: string = state.form.email();
return email.includes("@") && Boolean(name);
});
const handleAddContact = () => {
if (isFormValid()) {
const contacts = state.contacts;
const newId = contacts().length + 1;
contacts((prev) => [...prev, { id: newId, ...state.form() }]);
state.form({ email: "", name: "" });
}
};
return (
<div>
<h1>useFluentState - compute example</h1>
<input
value={state.form.name()}
onChange={(e) => state.form.name(e.target.value)}
placeholder="Enter name"
/>
<input
value={state.form.email()}
onChange={(e) => state.form.email(e.target.value)}
placeholder="Enter email"
/>
<button disabled={!isFormValid()} onClick={handleAddContact}>
Add contact
</button>
<ul>
{state.contacts().map((contact) => (
<li key={contact.id}>{contact.email}</li>
))}
</ul>
</div>
);
}
compute automatically tracks which proxy getters you calluseMemo + useEffect combinationscompute returns a function you call to read the current computed valuetype Todo = {
id: number;
title: string;
done: boolean;
};
function TodoApp() {
const [state, effect] = useFluentState({
todos: [
{ id: 1, title: "Learn fluent-state", done: false },
{ id: 2, title: "Build awesome apps", done: false },
],
filter: "all" as "all" | "done" | "active",
});
// Effect: Log when filtered todos change
effect(() => {
const visibleTodos = state.todos().filter((todo) => {
if (state.filter() === "done") return todo.done;
if (state.filter() === "active") return !todo.done;
return true;
});
console.log("Visible todos:", visibleTodos);
});
// Toggle todo done state
function toggleDone(id: number) {
state.todos((todos) =>
todos.map((todo) =>
todo.id === id ? { ...todo, done: !todo.done } : todo
)
);
}
// Change filter
function setFilter(value: "all" | "done" | "active") {
state.filter(value);
}
return (
<>
<h2>Todos</h2>
<div>
<button onClick={() => setFilter("all")}>All</button>
<button onClick={() => setFilter("active")}>Active</button>
<button onClick={() => setFilter("done")}>Done</button>
</div>
<ul>
{state.todos().map((todo) => (
<li key={todo.id}>
<label>
<input
type="checkbox"
checked={todo.done}
onChange={() => toggleDone(todo.id)}
/>
{todo.title}
</label>
</li>
))}
</ul>
</>
);
}
This example shows how useFluentState manages deeply nested arrays and objects with a fluent, immutable API, while effects automatically track dependencies and run only when needed.
Q: Why do I need to call state fields as functions like state.user.name()?
A: This getter function pattern allows automatic dependency tracking and controlled immutable updates, keeping your React components efficient.
Q: Can I update nested state immutably without writing verbose code?
A: Yes! useFluentState handles immutable updates under the hood, so you can write concise updates like state.user.address.city("New City").
Q: How do effects know when to re-run?
A: Effects track which state getters you call during execution. They only re-run when those specific values change.
Q: Does this work with arrays?
A: Absolutely. You can update arrays immutably and track changes as shown in the todo example.
computefluent-state is a stable and reliable library with a solid foundation.
While it currently lacks automated tests, it has been carefully designed and tested manually.
Adding automated test coverage is on the roadmap to ensure ongoing quality and reliability.
Contributions to help expand test coverage and improve robustness are very welcome!
Contributions, feedback, and ideas are welcome! Feel free to open issues or PRs.
MIT © Marcel Bos
Built with care by Marcel Bos
FAQs
Fluent, immutable, deeply reactive state for React — powered by Proxies and fine-grained tracking
We found that fluent-state demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.