Skip to content

Commit 289f0ca

Browse files
committed
Updates docs
1 parent 35a92c5 commit 289f0ca

File tree

1 file changed

+119
-42
lines changed

1 file changed

+119
-42
lines changed

README.md

Lines changed: 119 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ function TodoList() {
3636
// 👇 use hooks to get state or actions. you component will automatically
3737
// receive updated state
3838
const todos = useStore(state => state.todos.items)
39-
const add = useAction(actions => actions.todos.add)
39+
const add = useAction(dispatch => dispatch.todos.add)
4040
return (
4141
<div>
4242
{todos.map((todo, idx) => <div key={idx}>{todo.text}</div>)}
@@ -77,7 +77,7 @@ function TodoList() {
7777
- [Dispatching actions directly via the store](#dispatching-actions-directly-via-the-store)
7878
- [Creating an `effect` action](#creating-an-effect-action)
7979
- [Dispatching an `effect` action directly via the store](#dispatching-an-effect-action-directly-via-the-store)
80-
- [Deriving state](#deriving-state)
80+
- [Deriving state via `select`](#deriving-state-via-select)
8181
- [Accessing Derived State directly via the store](#accessing-derived-state)
8282
- [Final notes](#final-notes)
8383
- [Usage with React](#usage-with-react)
@@ -91,8 +91,8 @@ function TodoList() {
9191
- [effect(action)](#effectaction)
9292
- [select(selector)](#selectselector)
9393
- [StoreProvider](#storeprovider)
94-
- [useStore](#useStore)
95-
- [useAction](#useAction)
94+
- [useStore(mapState)](#usestoremapstate)
95+
- [useAction(mapAction)](#useactionmapaction)
9696
- [Prior Art](#prior-art)
9797

9898
---
@@ -247,9 +247,9 @@ store.dispatch.todos.saveTodo('Install easy-peasy').then(() => {
247247
})
248248
```
249249

250-
### Deriving state
250+
### Deriving state via `select`
251251

252-
If you have state that can be derived from state then you can use the [`select`](#select(selector)) helper. Simply attach it to any part of your model.
252+
If you have state that can be derived from state then you can use the [`select`](#selectselector) helper. Simply attach it to any part of your model.
253253

254254
```javascript
255255
import { select } from 'easy-peasy'; // 👈 import then helper
@@ -290,6 +290,10 @@ Oh! And don't forget to install the [Redux Dev Tools Extension](https://github.c
290290
291291
## Usage with React
292292
293+
With the new [Hooks](https://reactjs.org/docs/hooks-intro.html) feature introduced in React v16.7.0 it's never been easier to provide a mechanism to interact with global state in your components. We have provided two hooks, allowing you to access the state and actions from your store.
294+
295+
If you aren't familiar with hooks yet we highly recommend that you read the [official documentation](https://reactjs.org/docs/hooks-intro.html) and try playing with our [examples](#examples). Hooks are truly game changing and will simplify your components dramatically.
296+
293297
### Wrap your app with StoreProvider
294298
295299
Firstly we will need to create your store and wrap your application with the `StoreProvider`.
@@ -309,7 +313,7 @@ const App = () => (
309313
310314
### Consuming state in your Components
311315
312-
In order to use state within your components you can use the `useStore` hook.
316+
To access state within your components you can use the `useStore` hook.
313317
314318
```javascript
315319
import { useStore } from 'easy-peasy';
@@ -324,6 +328,8 @@ const TodoList = () => {
324328
};
325329
```
326330
331+
It's almost too easy that it doesn't require much explanation. We do however recommend that you read the API docs for the [`useStore` hook](#usestoremapstate) to gain a full understanding of the behaviours and pitfalls of the hook.
332+
327333
### Firing actions in your Components
328334
329335
In order to fire actions in your components you can use the `useAction` hook.
@@ -344,6 +350,8 @@ const AddTodo = () => {
344350
};
345351
```
346352
353+
For more on how you can use this hook please ready the API docs for the [`useAction` hook](#useactionmapaction).
354+
347355
### Alternative usage via react-redux
348356
349357
As Easy Peasy outputs a standard Redux store it is entirely possible to use Easy Peasy with the official [`react-redux`](https://github.com/reduxjs/react-redux) package.
@@ -641,42 +649,17 @@ A [hook](https://reactjs.org/docs/hooks-intro.html) granting your components acc
641649
642650
#### Arguments
643651
644-
You need to provide it with a function which is used to resolved the piece of state that your component requires. The function will receive the store's state and requires you to return back the piece of state you require.
645-
646-
You can either return a single value of state, or if you like an object containing multiple mappings of state (which is similar to the `connect` from `react-redux`).
647-
648-
For example, single piece of state:
649-
650-
```javascript
651-
const age = useStore(state => state.user.age)
652-
```
653-
654-
Or, multiple pieces of state via an object:
652+
- `mapState` (Function, required)
655653
656-
```javascript
657-
const { age, theme } = useStore(state => ({
658-
age: state.user.age,
659-
theme: state.preferences.theme
660-
}));
661-
```
662-
663-
#### A word of caution
654+
The function that is used to resolved the piece of state that your component requires. The function will receive the following arguments:
664655
665-
Please be careful in the manner that you map the state. Under the hood we use equality checking (===), similar to `react-redux` to determine if the mapped state has changed. If the equality check fails then your component will be "notified" of the change, receiving the updated state.
666-
667-
For example, wrapping your mapped state in an array is a bad idea as every time we tried to map your state a new array instance would be created and therefore the equality check would always break.
668-
669-
```javascript
670-
// BAD IDEA
671-
// 👇
672-
const state = useStore(state => [state.user.name, state.user.age])
673-
```
656+
- `state` (Object, required)
674657
675-
The only exception to the rule above is the case where you return an object against which you map multiple pieces of state. In this case we fall back to a shallow equality check between the old and new states before notifying your component of the changed state.
658+
The root state of your store.
676659
677-
So, rule of thumb is to never dervive new values/state within your mapState function. Remember you can leverage the (`select`)(#selectselector) helper against your store to help with derived state.
660+
Your `mapState` can either resolve a single piece of state. If you wish to resolve multiple pieces of state then you can either call `useStore` multiple times, or if you like resolve an object within your `mapState` where each property of the object is a resolved piece of state (similar to the `connect` from `react-redux`). The examples will illustrate the various forms.
678661
679-
#### Example
662+
#### Example
680663
681664
```javascript
682665
import { useStore } from 'easy-peasy';
@@ -691,7 +674,7 @@ const TodoList = () => {
691674
};
692675
```
693676
694-
If you wish to access multiple pieces of state in the same component you can make multiple calls to `useStore`.
677+
#### Example resolving multiple values
695678
696679
```javascript
697680
import { useStore } from 'easy-peasy';
@@ -708,9 +691,83 @@ const BasketTotal = () => {
708691
};
709692
```
710693
711-
### useAction
694+
#### Example resolving multiple values via an object result
695+
696+
```javascript
697+
import { useStore } from 'easy-peasy';
698+
699+
const BasketTotal = () => {
700+
const { totalPrice, netPrice } = useStore(state => ({
701+
totalPrice: state.basket.totalPrice,
702+
netPrice: state.basket.netPrice
703+
}));
704+
return (
705+
<div>
706+
<div>Total: {totalPrice}</div>
707+
<div>Net: {netPrice}</div>
708+
</div>
709+
);
710+
};
711+
```
712+
713+
#### A word of caution
714+
715+
Please be careful in the manner that you resolve values from your `mapToState`. To optimise the rendering performance of your components we use equality checking (===) to determine if the mapped state has changed.
716+
717+
When an action changes the piece of state your `mapState` is resolving the equality check will break, which will cause your component to re-render with the new state.
718+
719+
Therefore deriving state within your `mapState` in a manner that will always produce a new value (for e.g. an array) is an anti-pattern as it will break our equality checks.
720+
721+
```javascript
722+
// ❗️ Using .map will produce a new array instance every time mapState is called
723+
// 👇
724+
const productNames = useStore(state => state.products.map(x => x.name))
725+
```
726+
727+
You have two options to solve the above.
728+
729+
Firstly, you could just return the products and then do the `.map` outside of your `mapState`:
730+
731+
```javascript
732+
const products = useStore(state => state.products)
733+
const productNames = products.map(x => x.name)
734+
```
735+
736+
Alternatively you could use the [`select`](#selectselector) helper to define derived state against your model itself.
737+
738+
```javascript
739+
import { select, createStore } from 'easy-peasy';
740+
741+
const createStore = ({
742+
products: [{ name: 'Boots' }],
743+
productNames: select(state => state.products.map(x => x.name))
744+
});
745+
```
746+
747+
Note, the same rule applies when you are using the object result form of `mapState`:
748+
749+
```javascript
750+
const { productNames, total } = useStore(state => ({
751+
productNames: state.products.map(x => x.name), // ❗️ new array every time
752+
total: state.basket.total
753+
}));
754+
```
755+
756+
### useAction(mapAction)
712757
713-
A [hook](https://reactjs.org/docs/hooks-intro.html) granting your components access to the store's actions. You need to provide it with a function which is used to resolved the action that your component requires.
758+
A [hook](https://reactjs.org/docs/hooks-intro.html) granting your components access to the store's actions.
759+
760+
#### Arguments
761+
762+
- `mapAction` (Function, required)
763+
764+
The function that is used to resolved the action that your component requires. The function will receive the following arguments:
765+
766+
- `dispatch` (Object, required)
767+
768+
The `dispatch` of your store, which has all the actions mapped against it.
769+
770+
Your `mapAction` can either resolve a single action. If you wish to resolve multiple actions then you can either call `useAction` multiple times, or if you like resolve an object within your `mapAction` where each property of the object is a resolved action. The examples below will illustrate these options.
714771
715772
#### Example
716773
@@ -730,7 +787,27 @@ const AddTodo = () => {
730787
};
731788
```
732789
733-
If you wish to access multiple actions in the same component you can make multiple calls to `useAction`.
790+
#### Example resolving multiple actions via an object map
791+
792+
```javascript
793+
import { useState } from 'react';
794+
import { useAction } from 'easy-peasy';
795+
796+
const EditTodo = ({ todo }) => {
797+
const [text, setText] = useState(todo.text);
798+
const { saveTodo, removeTodo } = useAction(actions => ({
799+
saveTodo: actions.todos.save,
800+
removeTodo: actions.todo.toggle
801+
}));
802+
return (
803+
<div>
804+
<input value={text} onChange={(e) => setText(e.target.value)} />
805+
<button onClick={() => saveTodo(todo.id)}>Save</button>
806+
<button onClick={() => removeTodo(todo.id)}>Remove</button>
807+
</div>
808+
);
809+
};
810+
```
734811
735812
---
736813

0 commit comments

Comments
 (0)