diff --git a/README.md b/README.md
index 5a0becb..59b6473 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@ Differences with [freactal](https://github.com/FormidableLabs/freactal/):
- computed values are available in the `state` in effects
- `finalize` effect is triggered on unmount (symmetry with `initialize`)
- easy async effects
+- no state and effects injection (replace with [React context](https://reactjs.org/docs/context.html))
- async computed support
- update state by default: no need for `Object.assign()` and `{...state}`
- no [helper functions](https://github.com/FormidableLabs/freactal#helper-functions) (not necessary)
@@ -29,46 +30,69 @@ Installation of the [npm package](https://npmjs.org/package/reaclette):
```js
import React from "react";
import { render } from "react-dom";
-import { injectState, provideState } from "reaclette";
-
-const wrapComponentWithState = provideState({
- initialState: () => ({ counter: 0 }),
- effects: {
- addOne: () => (state, props) => ({ counter: state.counter + 1 }),
- },
- computed: {
- square: (state, props) => state.counter * state.counter,
+import { withStore } from "reaclette";
+
+const Component = withStore(
+ {
+ initialState: props => ({ counter: 0 }),
+ effects: {
+ addOne() {
+ this.state.counter += 1;
+ },
+ },
+ computed: {
+ square: (state, props) => state.counter * state.counter,
+ },
},
-});
-
-const Parent = wrapComponentWithState(() => );
-
-const Child = injectState(({ effects, state }) => (
-
-
Our counter is at: {state.counter}
-
Its squared value is: {state.square}
-
-
-
-
-));
+ (store, props) => (
+
+
Our counter is at: {store.state.counter}
+
Its squared value is: {store.state.square}
+
+
+
+
+ )
+);
-render(, document.body);
+render(, document.body);
```
## API
```js
-import { provideState, injectState } from "reaclette";
+import { withStore } from "reaclette";
```
-### `provideState(options) => (Component => Component)`
+### `withStore(options, renderFunction)`
Create a decorator that associates a React component with a store.
-`options` is an object which can contain the following properties.
+`renderFunction`
+
+A function that receives `store` and `props` in parameters and returns a `React node` like so:
+
+```js
+(store, props) => ReactNode;
+```
+
+- `store`
+ Is an object containing the following properties:
+
+ - `state`
+ - [`effects`](#effects)
+ - [`resetState`](#reset-state)
+
+- `props`
+ Passed inputs to React node
+
+`options`
-#### `initialState(props) => object`
+Is an object which can contain the following properties:
+
+#### Initial state
+
+`initialState(props) => object`
This function returns the initial state of store, which can be computed from the properties of the decorated component.
@@ -78,53 +102,86 @@ This function returns the initial state of store, which can be computed from the
}
```
-#### `effects: { [string]: Effect }`
+#### Effects
-These functions can be called from application code (see `injectState`) and can do side-effects and/or mutate the state.
+`effects: { [string]: Effect }`
-When called, an effect is provided with one or more arguments: a reference to other effects and any arguments passed to the effect from the application code.
+These functions can be called from application code and can do side-effects and/or mutate the state.
-An effect can return:
+When called, an effect is provided with one or more arguments: a reference to other effects and any arguments passed to the effect from the application code.
-1. nothing (`undefined` or `null`)
-2. an object containing new properties to merge in the state
-3. a promise resolving to one of the above
-4. a function which receives the state and the props then returns one of the above
+Effects can access effects, state (read and write) and props (at the time the effect was called) via `this`, which is extremely
+handy for async effects:
```js
-{
- effects: {
- incrementCounter: (effects) => (state, props) => ({ counter: state.counter + 1 }),
- onInputChange: (effects, event) => ({ counter: +event.target.value }),
- }
-}
+const Component = withStore(
+ {
+ initialState: () => ({
+ data: undefined,
+ loading: false,
+ }),
+ effects: {
+ loadData: async function() {
+ const { state } = this;
+ const { effects } = this;
+ if (state.data !== undefined || state.loading) {
+ return;
+ }
+
+ state.loading = true;
+ try {
+ state.data = await fetchData();
+ } finally {
+ state.loading = false;
+ }
+ },
+ },
+ },
+ (store, props) => (
+
+ )
+);
+
+render(, document.body);
```
-Effects can also access effects, state (read and write) and props (at the time the effect was called) via `this`, which is extremely
-handy for async effects:
+Effects are always asynchronous; therefore, they must always be awaited.
```js
-provideState({
- initialState: () => ({
- data: undefined,
- loading: false,
- }),
- effects: {
- loadData: async function() {
- const { state } = this;
- if (state.data !== undefined || state.loading) {
- return;
- }
-
- state.loading = true;
- try {
- state.data = await fetchData();
- } finally {
- state.loading = false;
- }
+const Component = withStore(
+ {
+ initialState: props => ({ counter: 0 }),
+ effects: {
+ async addOne() {
+ const { state } = this;
+ const { effects } = this;
+ state.counter += 1;
+ if (state.counter > 5) {
+ await effects.addBonus();
+ }
+ },
+ addBonus() {
+ this.state.counter += 5;
+ },
},
},
-});
+ (store, props) => (
+
+
Our counter is at: {store.state.counter}
+
+
+
+
+ )
+);
+
+render(, document.body);
```
There are two special effects:
@@ -134,7 +191,9 @@ There are two special effects:
Note that these effects are **not called** on server side!
-#### `computed: { [string]: Compute }`
+#### Computed
+
+`computed: { [string]: Compute }`
_Computeds_ are lazy values derived from the state and the properties of the decorated component.
@@ -153,27 +212,30 @@ They are automatically (re-)computed when necessary.
Compute functions can be async which is extremely useful to fetch data:
```js
-const CitySelector = provideState({
- computed: {
- // the computed is undefined in the render before the promise settles
- //
- // rejections are not handled and will possibly trigger an
- // unhandledRejection event on the window object, the computed will stay
- // undefined
- async cities({ country }) {
- const response = await fetch(`/countries/${state.country}/cities`);
- return response.json();
+const CitySelector = withStore(
+ {
+ computed: {
+ // the computed is undefined in the render before the promise settles
+ //
+ // rejections are not handled and will possibly trigger an
+ // unhandledRejection event on the window object, the computed will stay
+ // undefined
+ async cities({ country }) {
+ const response = await fetch(`/countries/${state.country}/cities`);
+ return response.json();
+ },
},
},
-})(
- injectState(({ onChange, state, effects, value }) => (
-