React Easy State: Mutable Stores and Proxy-Based Re-renders
Simple React state management. Made with ❤️ and ES6 Proxies.
At a glance
- What is it?
- React Easy State wraps plain objects in ES6 Proxies so that ordinary mutations trigger re-renders. It is a small library with two functions and two rules, and the trade-offs follow from that design.
- Who is it for?
- Adopt React Easy State if your team already thinks in mutable objects and you want re-renders to follow from ordinary property assignment, without reducers or action creators. Skip it if you need server-side rendering guarantees, a published stable release line, or a state layer that is not built on ES6 Proxies, since the README's platform support section is the place to check browser and environment coverage before you commit.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 156 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What React Easy State replaces, and for whom
Most React state libraries ask you to describe changes rather than perform them. You dispatch an action, or call a setter, and the library works out which components need to re-render. React Easy State inverts that. The README states the whole contract in two rules: always wrap components with view() and always wrap state store objects with store(). After that, the documentation says it does not matter how you structure or mutate your stores, because any syntactically valid code works.
The audience is narrow and specific. This suits developers who find reducer boilerplate heavier than the problem it solves, and who want state to look like ordinary JavaScript: nested objects, arrays, Maps, Sets, getters, setters, inheritance. The README explicitly lists those as valid store contents. It also fits codebases migrating incrementally, since a single component can be wrapped in view() without rewriting the rest of the app.
It is a poor fit for teams that want state changes to be enumerable events. If your debugging workflow depends on replaying a log of actions, mutable proxies give you nothing to replay. The library tracks reads and writes; it does not keep a history.
How the Proxy mechanism drives re-renders
The README describes a store as a transparent reactive proxy of the original object. That single sentence explains the architecture. When you call store(obj), you get back a Proxy that behaves like obj but intercepts property access. When a component wrapped in view() renders, its reads of store properties are recorded. Later writes to those same properties schedule a re-render of the components that read them.
This is why wrapping order matters, and the README gives an explicit warning about it. It shows a counter-example where a plain object is created and mutated, and only then passed to store(). That fails, because the mutation was aimed at the raw object, and the README states that mutating a raw, non-store-wrapped object will not schedule renders. The documented fix is to call store() at creation time and mutate the returned proxy thereafter.
The dependency tracking is per-property, not per-store. A component that reads only user.name will not re-render when user.age changes, because the proxy records which keys were touched during render. That granularity is the reason the library can stay this small: there is no selector function to write and no memoization to configure.
Two more mechanisms appear in the README. batch(fn) groups updates so that intermediate states do not each trigger a render pass. autoEffect(fn) and clearEffect(fn) let you attach side effects that run when the state they read changes, which is the escape hatch for work that should not live inside a component.
Installing React Easy State and writing a first store
The package is published as @risingstack/react-easy-state. The README gives a single install command, and the package.json lists dist/cjs.es6.js as the main entry and dist/es.es6.js as the module entry, with types/index.d.ts for TypeScript.
npm install @risingstack/react-easy-stateThe README also documents a quick project setup using Create React App, which it says works without additional configuration. It notes that npx requires npm 5.2 or later.
npx create-react-app my-app
cd my-app
npm install @risingstack/react-easy-state
npm startWith the package installed, the smallest useful program is the counter from the README. A store holds a number and a method that mutates it; the component reads the number and calls the method on click.
import React from 'react';
import { store, view } from '@risingstack/react-easy-state';
const counter = store({
num: 0,
increment: () => counter.num++
});
export default view(() => (
<button onClick={counter.increment}>{counter.num}</button>
));Clicking the button calls increment, which assigns to counter.num through the proxy. The view re-renders and the label updates. There is no setState call and no reducer. If the label does not change, the most likely cause is the wrapping-order mistake described above: the object was mutated before it was passed to store().
Stores can also hold async logic directly. The README shows a store method that awaits a fetch and assigns the result back onto the store, which then triggers the same render path as any other write.
Where the design gets in your way
The two-rule contract is genuinely small, but it pushes complexity into places the README does not cover. Because stores are plain mutable objects, nothing stops one module from writing to another module's store at an arbitrary time. The library will faithfully re-render, but it will not tell you who made the change. Teams used to tracing a dispatch back to its call site lose that thread.
Proxy interception only covers property access that goes through the proxy. The README is explicit that mutating the raw object does not schedule renders, which means any code path that keeps a reference to the pre-store object becomes a silent bug. Passing the raw object into a third-party library that stores it internally is the classic way to hit this.
The README also carries a warning worth treating as a maintenance signal. It states that v6.3.0 fixed a bug that could render zombie children, and asks readers to update to that version at least. A rendering bug of that class is the kind that is hard to reproduce and easy to ship.
Finally, the release history does not show a stable line past v6.3.0. The recent releases listed are v6.3.1-alpha.1 from 2021-01-28, v7.0.0-alpha.1 from 2020-05-21, and v6.4.0-alpha.1 from 2020-04-26, all pre-releases. The repository's last push was on 2026-04-30, so there is activity, but the published tags do not show a corresponding stable release. Anyone pinning versions should read the npm page rather than assume the alpha tags reflect what ships by default.
React Easy State against Redux and MobX
The closest comparison the README itself invites is with Redux, since it links to the React Redux documentation when describing the zombie children bug. The difference is in what you write. Redux requires actions, reducers and a dispatch call, and in exchange you get a serializable log of every transition and time-travel debugging. React Easy State requires store() and view(), and gives you mutable state with no transition log at all. If you need to reproduce a bug by replaying user actions, Redux is the tool and this library is not.
MobX is the nearer neighbour in mechanism. Both track property reads and re-render only the components that touched the changed data. MobX asks you to mark observable state and computed values with decorators or explicit calls, and it has its own reaction and action concepts. React Easy State's README claims a smaller surface: two functions, two rules, and stores that are valid JavaScript objects including Maps, Sets and getters. The cost of that smaller surface is fewer tools for reasoning about why a change happened.
Plain React state with hooks is the third option, and for a small component tree it is the right one. useState and useReducer keep state local and require no library. React Easy State earns its install when state is shared across components that are not near each other in the tree, and when threading setters through props has become the larger problem.
Maintenance, licensing and upgrade cost
The package is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are included. That is the standard permissive arrangement; the LICENSE file at the repository root is the authoritative text, and nothing here is legal advice.
The repository is not archived, and its last push was on 2026-04-30. That date is recent enough that the codebase is being touched, but the release tags tell a different story: the most recent listed release is an alpha from 2021-01-28. A repository can receive commits without publishing a stable version, and that gap is the practical risk for anyone adopting this. You may be running code that is newer than the last stable tag, or older, depending on what npm resolves.
Upgrade cost is low in one respect and awkward in another. The public API is five functions according to the README's summary: store, view, batch, autoEffect and clearEffect. A surface that small is unlikely to break often. But the package.json ships dist and types only, with main pointing at dist/cjs.es6.js and module at dist/es.es6.js. Both filenames carry an es6 suffix, and the library is built on ES6 Proxies, so the upgrade question is less about API churn and more about whether your target environments support the runtime features the build assumes. The README has a platform support section for exactly that, and it is the first thing to read before committing.
Editorial conclusion
Adopt React Easy State if your team already thinks in mutable objects and you want re-renders to follow from ordinary property assignment, without reducers or action creators. Skip it if you need server-side rendering guarantees, a published stable release line, or a state layer that is not built on ES6 Proxies, since the README's platform support section is the place to check browser and environment coverage before you commit. Verify two things first: that your build toolchain resolves the package's module and main entries without extra configuration, and that the version you install is at least 6.3.0, because the README states that release fixed a bug that could render zombie children.
Frequently asked questions
How do I install React Easy State?
Run npm install @risingstack/react-easy-state. The README also shows a Create React App setup where you run npx create-react-app my-app, install the package inside the project, and start the dev server, noting that npx needs npm 5.2 or later.
Why does mutating my React Easy State store not re-render the component?
The README states that mutating a raw, non-store-wrapped object will not schedule renders. Wrap the object with store() at creation time and mutate the returned proxy instead of holding a reference to the original.
What does view() do in React Easy State?
view() wraps a component so that the store properties it reads during render are tracked. When one of those properties is written through the store proxy, the wrapped component re-renders.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/risingstack-react-easy-state)