Skip to content

Commit 958baaa

Browse files
committed
docs (readme): update the readme
1 parent e777e07 commit 958baaa

File tree

4 files changed

+96
-71
lines changed

4 files changed

+96
-71
lines changed

README.md

Lines changed: 93 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,40 @@
44

55
[![CircleCI](https://circleci.com/gh/solkimicreb/react-easy-state/tree/master.svg?style=shield)](https://circleci.com/gh/solkimicreb/react-easy-state/tree/master) [![Coverage Status](https://coveralls.io/repos/github/solkimicreb/react-easy-state/badge.svg)](https://coveralls.io/github/solkimicreb/react-easy-state) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) [![Version](https://img.shields.io/npm/v/react-easy-state.svg)](https://www.npmjs.com/package/react-easy-state) [![License](https://img.shields.io/npm/l/react-easy-state.svg)](https://www.npmjs.com/package/react-easy-state)
66

7-
Easy State a tiny state management solution for React with a strong focus on simplicity.
7+
*Easy State provides a healthy balance of local and global state management in a simple, scalable way.*
88

99
</center>
1010

11+
## Table of contents
12+
13+
- [Installation](#installation)
14+
- [Usage](#usage)
15+
- [Examples](#examples)
16+
- [Platform support](#platform-support)
17+
- [Performance](#performance)
18+
- [State management overview](#state-management-overview)
19+
- [How does it work?](#how-does-it-work)
20+
- [Contributing](#contributing)
21+
- [The NX Framework](#the-nx-framework)
22+
1123
## Installation
1224

13-
`npm i react-easy-state --save`
25+
`npm install react-easy-state`
1426

1527
## Usage
1628

17-
Easy State consists of two functions: `easyComp` and `easyStore`.
29+
Easy State consists of two functions:
30+
31+
- `easyComp` makes React's own component level state management simpler.
32+
- `easyStore` creates global state stores for complex apps.
1833

1934
### easyComp
2035

21-
Wrapping your components with the `easyComp` function simplifies React's own state management in the following ways.
36+
Wrapping your components with the `easyComp` function provides the following benefits.
2237

2338
- It allows you to mutate the component's state directly, without calling `setState`.
2439

25-
- It binds your component's methods to the component, to allow them to be passed as callbacks.
40+
- It binds your component's methods to the component.
2641

2742
```js
2843
import React, { Component } from 'react'
@@ -57,102 +72,84 @@ class Hello extends Component {
5772
export default easyComp(Hello)
5873
```
5974

60-
Apart from the simplified syntax wrapping your component's with `EasyComp` provides the following benefits.
75+
*Make sure to wrap all of your components - including stateful and stateless ones - before you export them.*
6176

62-
- The state is just an object, which updates synchronously when you update it. You don't have to worry about the asynchronous nature of `setState`.
77+
In addition to the boilerplate reduction, `easyComp` comes with a bunch of additional benefits that may not be obvious at first glance.
6378

64-
- State mutations are picked up and they trigger the render method when appropriate.
79+
- The state becomes a simple object, which updates synchronously. You don't have to worry about immutable state updates or the asynchronous nature of `setState`. If you are not sure about the meaning of this check out [this article](https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b) about `setState`.
6580

66-
- The render method is only triggered if it is affected by state mutations. If it doesn't use the mutated part of the state or the mutation doesn't change the state, the render method is not triggered.
81+
- The render method is only triggered if it is affected by the state mutations. If it doesn't use the mutated part of the state or the mutation doesn't change the state, the render method is not triggered.
6782

6883
- The render method is never executed immediately. Triggered renders are collected and executed asynchronously in one batch.
6984

70-
- Duplicates renders are removed. A render never runs twice in one batch - no matter how many times it got triggered. Renders run in first trigger order.
85+
- Renders always run before the next repaint.
86+
87+
- Duplicates renders are removed. A render never runs twice in one batch - no matter how many times it got triggered.
7188

7289
- Renders may trigger others renders by mutating the state. In this case loops are automatically resolved.
7390

74-
- Renders always run before the next repaint.
75-
76-
- Easy State implements an optimal `shouldComponentUpdate` for your components, so you don't have to worry about doing it by hand.
91+
- Easy State implements an optimal `shouldComponentUpdate` for your components.
7792

78-
As a result the state is always fresh and a stable and fresh view is always achieved before the next repaint with the minimal number of required renders.
93+
As a result the state is always fresh and a stable and a fresh view is always achieved before the next repaint with the minimal number of required renders.
7994

8095
### easyStore
8196

82-
`EasyStore` creates global state stores, to handle data that do not fit into component state. Wrapping an object with `easyStore` has to following effects.
97+
`easyStore` creates global state stores, to handle data that do not fit into component state. Wrapping an object with `easyStore` has to following effects.
8398

8499
- It transforms the object into a reactive data store, which triggers appropriate renders on mutations.
85100

86-
- It binds your object's methods to the object, to allow them to be passed as callbacks.
101+
- It binds your object's methods to the object.
87102

88103
```js
89104
import React, { Component } from 'react'
90105
import { easyComp, easyStore } from 'react-easy-state'
91106

92107
// this creates a global state store
93108
const store = easyStore({
94-
name: 'Hello'
109+
name: 'Hello',
110+
// this is bound to the object, so it can be safely passed as a callback
111+
setName (ev) {
112+
this.name = ev.target.value
113+
}
95114
})
96115

97-
class Hello extends Component {
98-
// this is bound to the component, so it can be safely passed as a callback
99-
onChange (ev) {
100-
store.name = ev.target.value
101-
}
116+
// the render is triggered whenever store.name changes
117+
function Hello () {
118+
const { name, setName } = store
102119

103-
// the render is triggered whenever store.name changes
104-
render () {
105-
return (
106-
<div>
107-
<input value={store.name} onChange={this.onChange} />
108-
<div>Hello {store.name}!</div>
109-
</div>
110-
)
111-
}
120+
return (
121+
<div>
122+
<input value={name} onChange={setName} />
123+
<div>Hello {name}!</div>
124+
</div>
125+
)
112126
}
113127

114128
// the component must be wrapped with easyComp
115129
export default easyComp(Hello)
116130
```
117131

118-
`easyStore` creates global state stores. A store is just an object and it behaves much like the component state from `easyComp`. It can be handled like a normal JavaScript object and it automatically triggers the appropriate render methods when it is mutated.
119-
120-
## State management tutorial
121-
122-
Easy State promotes a healthy balance between local and global state. The following use cases will give a rough guide when to use which.
123-
124-
### Widgets and libraries
125-
126-
This is an easy decision. Always use local component state for reusable components. They should be robust and versatile without implicit dependencies. Check out the introductory [clock example]() for some code.
132+
*Make sure to wrap your component with `easyComp` even if it uses global stores only and no local state.*
127133

128-
### Application state
129-
130-
Most application state is usually persistent and singleton. It should be managed in global stores.
134+
- Global stores are simple objects and there is no limitation on what you can do with them. As an example feel free to use expando properties, arrays, deeply nested objects, ES6 collections or getters/setters in your stores.
131135

132-
- A good example is the currently logged in user. There is only one user at a time and user data should be easily available anywhere anytime. Perfect candidate for singleton global state.
136+
- Render methods are only triggered if they are affected by the store mutations. If they don't use the mutated part of the store or the mutation doesn't change the store, the render method is not triggered.
133137

134-
- Another nice example is user inputs, which should go into the URL or change the browser history. These are inherently global because they affect global concepts (URL and browser history). Some example for these are filters, date ranges and sorting primitives.
138+
- Render methods are never executed immediately. Triggered renders are collected and executed asynchronously in one batch.
135139

140+
- Renders always run before the next repaint.
136141

137-
### Application pages
142+
- Duplicates renders are removed. A render never runs twice in one batch - no matter how many times it got triggered.
138143

139-
A typical app has several independent pages. There is only one active page at a time, which makes them a nice candidate for singleton global state stores. However pages are not as persistent as the app user for example, which makes them lean towards local state management.
144+
- Renders may trigger others renders by mutating the store again. In this case loops are automatically resolved.
140145

141-
Page state usually has filters and inputs, which goes into the URL and browser history. In this case it is inherently global and it should be handled in a global state store. These stores persist between page transitions, but this is perfectly fine. As a bonus you get a faster transition, because you don't always have to re-fetch all of the data. If you do not want data to linger around clean up the relevant parts in `componentWillUnmount`.
146+
As a result the stores are always fresh and a stable and a fresh view is always achieved before the next repaint with the minimal number of required renders.
142147

143148
## Examples with live demos
144149

145-
- [Clock Widget](https://solkimicreb.github.io/react-easy-state/examples/clock/) ([source](/examples/clock/))
146-
- [TodoMVC](https://solkimicreb.github.io/react-easy-state/examples/todoMVC/) ([source](/examples/todoMVC/))
147-
- [Contacts Table](https://solkimicreb.github.io/react-easy-state/examples/contacts/) ([source](/examples/contacts/))
148-
149-
## Performance
150-
151-
You can compare Easy State with plain React and other state management libraries with the below benchmarks. Easy State performs a bit better than MobX, a bit worse than plain optimized React and similarly to Redux.
152-
153-
- [js-framework-benchmark](https://github.com/krausest/js-framework-benchmark) ([source](https://github.com/krausest/js-framework-benchmark/tree/master/react-v15.5.4-easy-state-v1.0.3)) ([results](https://rawgit.com/krausest/js-framework-benchmark/master/webdriver-ts-results/table.html))
154-
155-
The list of benchmarks will expand in the future.
150+
- [Clock Widget](https://solkimicreb.github.io/react-easy-state/dist/clock/clock.html) ([source](/examples/clock/))
151+
- [TodoMVC](https://solkimicreb.github.io/react-easy-state/dist/todoMVC.html) ([source](/examples/todoMVC/))
152+
- [Contacts Table](https://solkimicreb.github.io/react-easy-state/dist/contacts.html) ([source](/examples/contacts/))
156153

157154
## Platform support
158155

@@ -165,14 +162,48 @@ The list of benchmarks will expand in the future.
165162
- React native is not yet supported
166163
- IE is not supported
167164

165+
## Performance
166+
167+
You can compare Easy State with plain React and other state management libraries with the below benchmarks. Easy State performs a bit better than MobX, a bit worse than plain optimized React and similarly to Redux.
168+
169+
- [js-framework-benchmark](https://github.com/krausest/js-framework-benchmark) ([source](https://github.com/krausest/js-framework-benchmark/tree/master/react-v15.5.4-easy-state-v1.0.3)) ([results](https://rawgit.com/krausest/js-framework-benchmark/master/webdriver-ts-results/table.html))
170+
171+
The list of benchmarks will expand in the future.
172+
173+
## State management overview
174+
175+
Finding the right balance between local component state and global state stores is not always a trivial task. This section gives you some general guide lines when to use which.
176+
177+
### Reusable widgets
178+
179+
This is an easy decision. Always use local component state for reusable components. Depending on global stores would interfere with their reusability and break them. Check out the introductory [clock example](/examples/clock/) for some code.
180+
181+
### Application state
182+
183+
Application state should usually be managed in global stores. It is singleton and its is usually persistent while the app is open. You can find a few candidates for global storage below.
184+
185+
- The currently logged in user is a good example. There is only one user at a time and user data should be easily available anywhere anytime. It is a perfect candidate for a singleton global store.
186+
187+
- User inputs, which should go into the URL or change the browser history are also great examples. These are inherently global because they affect global concepts - like the URL and browser history. Some example for these are filters, date ranges and sorting primitives.
188+
189+
Not everything fits in global stores though. You can find a few cases below when using the local component state makes more sense then global stores.
190+
191+
- Utility and meta data should go into component state. For example you may have a component which handles recent history for an input field. It may make sense to receive the data for the input from a global store and manage the history meta data in the local state. Check out the [contacts table example](/examples/contacts/) for some code.
192+
193+
### Application pages
194+
195+
Application pages deserve a special mention. A typical app has several pages but, only one of them is active at a time. This makes them a nice candidate for singleton global state stores. However pages are not as persistent as the app's user for example, which makes them lean towards local state management.
196+
197+
Page state usually has properties, which belong in the URL and browser history. In this case it is inherently global and it should be handled in a global store. These stores persist between page transitions, but this is perfectly fine. As a bonus you get a faster transition, because you don't always have to re-fetch all of the data. If you do not want data to linger around clean up the relevant parts in `componentWillUnmount`.
198+
168199
## How does it work?
169200

170-
Under the hood it uses the [@nx-js/observer-util](https://github.com/nx-js/observer-util) library, which relies on ES6 Proxies to observe state changes. Thanks to the Proxies it doesn't have edge cases or limitations. You can write any JS code without worrying about the render function. [This blog post](https://blog.risingstack.com/writing-a-javascript-framework-data-binding-es6-proxy/) gives a little sneak peek under the hood of the `observer-util`.
201+
Under the hood Easy State uses the [@nx-js/observer-util](https://github.com/nx-js/observer-util) library, which relies on ES6 Proxies to observe state changes. Thanks to the Proxies it doesn't have edge cases or limitations. You can write any JS code without worrying about the render function. [This blog post](https://blog.risingstack.com/writing-a-javascript-framework-data-binding-es6-proxy/) gives a little sneak peek under the hood of the `observer-util`.
171202

172203
## Contributing
173204

174-
Contributions are always welcome. Just send a PR against the master branch or open a new issue. Please make sure that the tests and the linter pass and the coverage remains at 100%. Thx!
205+
Contributions are always welcome. Just send a PR against the master branch or open a new issue. Please make sure that the tests and the linter pass and the coverage remains decent. Thanks!
175206

176207
## The NX Framework
177208

178-
This library is a side effect of the front-end framework I have been working on in the past year. Please take a look at the [NX Framework](https://nx-framework.com/) if you have some time. Have a nice day!
209+
This library is a side project of the front-end framework I have been working on in the past year. Please take a look at the [NX Framework](https://nx-framework.com/) if you have some time. Thanks and have a nice day!

examples/clock/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# Clock widget
2-
3-
**[Live Demo](https://solkimicreb.github.io/react-easy-state/examples/clock/)**
1+
# Clock widget · [Live Demo](https://solkimicreb.github.io/react-easy-state/dist/clock.html)
42

53
A super simple digital clock widget. It uses component state only and no global store, which is the general rule for **reusable** components and widgets.

examples/contacts/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# Contact Table
2-
3-
**[Live Demo](https://solkimicreb.github.io/react-easy-state/examples/contacts/)**
1+
# Contact Table · [Live Demo](https://solkimicreb.github.io/react-easy-state/dist/contacts.html)
42

53
An editable contact list, which demonstrates how to balance between local component state and global stores. It manages the central data and logic in a global store and keeps temporary utility data in component states. Dependency injection is handled by plain imports and react props.

examples/todoMVC/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# TodoMVC
2-
3-
**[Live Demo](https://solkimicreb.github.io/react-easy-state/examples/todoMVC/)**
1+
# TodoMVC · [Live Demo](https://solkimicreb.github.io/react-easy-state/dist/todoMVC.html)
42

53
This is a lighter version of the well-known React [TodoMVC](http://todomvc.com/). It handles all of the state in a separate global store and relies heavily on JS getters and setter to provide a clear and simple state management. Be sure to click all the buttons in the demo that you can find.

0 commit comments

Comments
 (0)