# Every react-easy-state tag is an alpha and the newest is from 2021

> A two-function React state library built on ES6 Proxies whose entire published history is three alpha tags, the newest from January 2021, against a default branch last pushed 2026-04-30. Its manifest version is a development placeholder filled in by semantic-release, its headline fix recommendation points at a version older than the newest tag, and a seventh-major alpha predates the sixth-major one.

**RisingStack/react-easy-state** — Simple React state management. Made with ❤️ and ES6 Proxies.

- Repository: https://github.com/RisingStack/react-easy-state
- Stars: 2,538 · Forks: 106
- Language: JavaScript
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/risingstack-react-easy-state

## Three tags, all prereleases, the newest from January 2021

The release history is short enough to state completely. There are three tags.

`v6.3.1-alpha.1` from 2021-01-28. `v7.0.0-alpha.1` from 2020-05-21. `v6.4.0-alpha.1` from 2020-04-26.

Every one carries an alpha suffix. There is no stable release in the history at all.

The ordering is the more interesting part. The seventh-major alpha was published in May 2020, and the sixth-major alpha in April 2020, but the next sixth-major tag did not appear until January 2021, eight months after the seventh existed. So the numbering does not tell you which line of work is newer, and the seventh major was abandoned rather than shipped.

The manifest does not help pin any of this, because its version field is a placeholder:

```json
"version": "0.0.0-development",
```

That is what a semantic-release setup leaves in the file between releases, with the real version written in at publish time from the commit message. So the version in the repository is always zero and the version on the registry is whatever the last successful release produced.

Meanwhile the default branch took its last push on 2026-04-30. There is code moving and nothing being published, which is the shape of a project whose maintenance has become incidental.

## The pinned-version advice points below the newest tag

The most prominent piece of news on the front page is a version recommendation.

It says that version 6.3.0 fixed a bug that could render zombie children, linking to the React Redux documentation on that failure mode, and asks readers to update to that version at least.

Three things about that sentence.

It is the loudest thing on the page, which tells you the bug was serious. Zombie children, in that context, means a component rendering against props that a parent has already replaced, so a view keeps showing data that is no longer current. For a library whose entire claim is automatic re-rendering, that is exactly the failure that would undermine the premise.

It names 6.3.0. The newest tag is `v6.3.1-alpha.1`, which is a later version and also an alpha. So the minimum being recommended is a stable release below the newest thing published.

And there is a `v7.0.0-alpha.1` in the history, so the reader is choosing between a stable 6.3.0, a later sixth-major alpha, and a seventh-major alpha that predates both.

None of this is stated. The page presents 6.3.0 as a floor and leaves the rest of the version landscape to the tags page, which is the opposite order of what a reader needs when a library's release history is three prereleases deep.

## Two documented traps, both about the raw object

The library is two functions and two rules: always wrap components with `view`, always wrap state objects with `store`.

The rules exist because of two specific ways to get it wrong, and both are documented with a wrong and a right version.

The first is ordering. This does not work:

```js
// DON'T DO THIS
const person = { name: 'Bob' };
person.name = 'Ann';

export default store(person);
```

and this does:

```js
// DO THIS INSTEAD
const person = store({ name: 'Bob' });
person.name = 'Ann';

export default person;
```

The reason given is that mutating the raw object before wrapping schedules no renders, because the mutation targeted something that was not yet a proxy. The library does not throw here. It simply does nothing, which is the worst shape of failure: no error, and a view that stays stale.

The second trap is `this`. Store methods are passed around as callbacks, so `this` is not the store:

```js
const counter = store({
  num: 0,
  increment() {
    // DON'T DO THIS
    this.num++;
    // DO THIS INSTEAD
    counter.num++;
  },
});
```

So a store method has to reference the store by name rather than by receiver. That is an unusual constraint for JavaScript and it means the idiomatic object-literal pattern has to be written with a forward reference back to the const it is being assigned to.

## Reactivity is a Proxy, and there is a build without hooks

The mechanism is ES6 Proxies, which is stated in the package description and is the whole design.

A store is described precisely as a transparent reactive proxy of the original object. You pass in a plain object and get back something that behaves the same way, except that reads inside a reactive view are tracked and writes schedule updates.

What gets tracked is broader than property assignment. The documented examples include assigning to a nested field, deleting a property, pushing onto an array, and calling `set` on a `Map`. The store can hold nested objects, arrays, Maps, Sets, getters, setters, and inheritance, and the claim is that it may be mutated in any syntactically valid way. Async methods work with ordinary `await`, and a store may import and use other stores.

Proxies are why there are three builds. The test scripts run three separate Jest configurations: one for the web build, one for a build without hooks, and one for native. The native configuration is not incidental, since one of the seven examples is a native clock. A Proxies-based reactivity layer and React Native is a combination worth verifying on your own target rather than assuming.

Effects are managed explicitly rather than through a lifecycle. The API summary lists `batch` to group updates, and `autoEffect` paired with `clearEffect` to register and remove a side effect. A manual register and remove pair is what a no-hooks build gives you, and it means effect lifetime is your responsibility.

## Pre-push runs three suites and lint skips the examples

The git hooks are configured in the manifest rather than in a hooks directory, which is the older Husky layout, and the commit hook still passes a variable that later versions of that tool removed.

Three hooks are wired. Pre-commit runs lint. Commit-msg runs commitlint with the git parameters variable. Pre-push runs `npm t`, which is the full test command.

That test command is not one suite. It chains the web run, the web run with hooks disabled, and the native run, each with its own Jest configuration. So every push, not every commit, executes three test configurations end to end. That is a heavy default for a hook, and it is the sort of thing that gets removed by a contributor the first time it makes a push take four minutes.

The lint invocation is narrower than it looks:

```
eslint --max-warnings 0 --ext js,jsx src scripts __tests__ __mocks__
```

It covers the source, the build scripts, the tests, and the manual mocks. It does not cover `examples/` or `types/`, which means the seven example applications and the type declarations are outside the gate even though they are in the repository and the type declarations are what consumers actually see.

The zero-warning tolerance is the part worth keeping. Treating warnings as failures in a library this small is a reasonable signal, and it is enforced on the directories that matter.

## The npm badge appears twice inside the contributors markers

The badge row at the top has a structural problem that survives in the rendered page.

The badges are a Circle CI build badge, a dependency review badge, a coverage service badge with a branch query, a bundle size badge, and then the npm package badge. The npm badge is there twice, both pointing at the same package.

And the duplication is not innocent. The row is wrapped in HTML comment markers that delimit a section for the contributors badge generator, with instructions not to remove or modify it. The two npm badges sit between the start marker and the end marker, so a tool that regenerates the contributors block treats the npm badges as its content.

So the sequence runs: start marker, two npm badges, end marker, and then an empty contributors link below. The generator that produced those markers has inserted its output in the wrong place, and running it again would be a small act of faith.

The badge targets tell their own story about age. The dependency review badge points at a service that has been largely superseded by the pull-request tooling the major package hosts now provide, and the coverage badge carries an explicit branch query, which was the convention before coverage reporting moved into the build status itself.

None of this affects the package. It affects whether you can read the front page as a status page.

## Anchors carry emoji shortcodes and the setup link is retired

Every section heading on the page ends in an emoji shortcode, and the table of contents entries carry the matching suffix in their anchor links. So the link for the introduction section includes the shortcode in its target rather than just the title text.

The table of contents is generated, not written. There is a script for it, a script entry to run it, and HTML comment markers around the generated region with an instruction not to edit it by hand. That is good practice, and it is also why the emoji appears in the anchors: the generator carried the heading text through verbatim.

The practical consequence is that those anchors are coupled to the decorative part of the headings. Strip the emoji from a heading, which is a routine edit, and every link into that section stops resolving. The same coupling would break if the emoji were removed for a documentation rewrite.

The quick project setup block has its own vintage. It points at Create React App under the `facebookincubator` organisation, which is the old location, and the surrounding note says you need npm 5.2 or newer to use `npx`, which dates the guidance to the era when `npx` first shipped.

So the fastest path on the page, the three commands that scaffold a working project, points at a tool that has moved and a version requirement from a specific point in npm's history.

## Seven examples, and two of them are clocks

The examples directory holds seven applications: a beer finder, a clock, a contacts list, a native clock, a pokedex, a stopwatch, and the TodoMVC implementation the introduction links to.

Two of the seven are timers. That is not padding. A Proxies-based reactivity layer has to behave correctly when a value changes on a schedule rather than in response to a user action, and clock and stopwatch examples are the cheapest way to expose a store that re-renders at the wrong time or not at all. It is also the failure mode a demo hides most easily, because a demo driven entirely by clicks never has to survive an interval.

The native clock is the one that carries the most risk, since it pairs Proxies with React Native rather than with a browser.

The build system reflects how many of these there are. There are separate scripts to install the examples and to build them, and the example server has its own script, so examples are part of the release process rather than a folder you set up by hand.

Two other scripts are worth noticing because they test something unusual. One runs the test suite against Node, which for a React library means checking the module loads outside a browser. Another runs the built artifacts, which catches the class of problem where the source passes and the bundle does not. For a library publishing separate CommonJS and ES module builds, that second script is the more valuable of the two.

## Conclusion

react-easy-state is attractive if you want readable state code with no reducer boilerplate and you already accept Proxies, because the entire contract is two wrappers and everything else follows from that. Four things to check before you adopt it. Reactivity is implemented with ES6 Proxies, which is a hard runtime requirement rather than something a polyfill can cover, so your browser and bundler targets decide whether it works at all. Every published tag is a prerelease, the newest from January 2021, so there is no stable version to pin and you are adopting an alpha whose README still recommends a different, older version as a minimum. The effect model is manual, with functions to register and clear side effects rather than a lifecycle hook. And there are two documented traps: mutating an object before wrapping it, and using `this` inside a store method. If you are starting a new React project in 2026, weigh this against the current mainstream options first, because the release history here has been static for years.

## FAQ

### What is the API of @risingstack/react-easy-state?

Two functions and two rules. `store(obj)` wraps a state object and `view(Comp)` wraps a component, and the rules are to always wrap components with view and always wrap store objects with store. The rest of the API is `batch(fn)` for grouping updates, plus `autoEffect(fn)` and `clearEffect(fn)` for side effects.

### How does @risingstack/react-easy-state detect changes?

With ES6 Proxies. A store is a transparent reactive proxy of the object you pass, so reads inside a reactive view are tracked and writes schedule updates. Nested fields, deletes, array pushes, Map and Set operations, getters, and setters are all handled.

### What are the known footguns in @risingstack/react-easy-state?

Two are documented. Mutating an object before passing it to store schedules no renders, because the mutation targeted the raw object. And `this` inside a store method does not work, because the method is passed as a callback and loses its `this`, so the store must be referenced by name.

### Which builds and test configurations does @risingstack/react-easy-state have?

Three Jest configurations, covering the web build, a build without hooks, and native. The test command runs all three in sequence, and a pre-push hook runs that full command.

### What version of @risingstack/react-easy-state should I use?

There is no stable tag to choose. The manifest version is a `0.0.0-development` placeholder written at publish time by semantic-release, the newest tag is `v6.3.1-alpha.1` from 2021-01-28, and a `v7.0.0-alpha.1` from 2020-05-21 also exists. The README asks for at least v6.3.0 for a zombie-children fix.

## Sources

- [Issues](https://github.com/RisingStack/react-easy-state/issues)
- [License: MIT](https://github.com/RisingStack/react-easy-state/blob/master/LICENSE)
- [README](https://github.com/RisingStack/react-easy-state/blob/master/README.md)
- [Releases](https://github.com/RisingStack/react-easy-state/releases)
- [RisingStack/react-easy-state on GitHub](https://github.com/RisingStack/react-easy-state)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/risingstack-react-easy-state
