Skip to content

Commit

Permalink
update vanilla redux example to make an example of listener not updat…
Browse files Browse the repository at this point in the history
…ed when state or nested state mutated
  • Loading branch information
euZebe committed May 13, 2018
1 parent f1d0032 commit 52afafa
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 193 deletions.
50 changes: 42 additions & 8 deletions src/molkky-vanilla-redux.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ function rootReducer(state = {}, action) {
}

// else, several pins falled

// TODO: toggle this block and the next one to make reducer unpure
const nextPlayerState = {
...previousPlayerState,
score: previousPlayerState.score + fallenPins.length,
Expand All @@ -83,6 +85,18 @@ function rootReducer(state = {}, action) {
[player]: nextPlayerState
};

// FIXME: unpure reducer: state mutated => audience and referee won't see the action
// state[player].score += fallenPins.length;
// state[player].consecutiveFailures = 0;
// state.fallenPins = fallenPins.length;
// return state;

// FIXME: unpure reducer: nested state mutated => player listener won't see the action
// const nextState = { ...state, fallenPins: fallenPins.length };
// nextState[player].consecutiveFailures = 0;
// nextState[player].score += fallenPins.length;
// return nextState;

default:
return state;
}
Expand All @@ -91,32 +105,52 @@ function rootReducer(state = {}, action) {
// init store
const store = createStore(rootReducer);

// define listeners
/**
* Listener which keeps a cache of the player state, executing itself only on new player state
* @param name
* @returns {Function}
* @constructor
*/
function PlayerListener(name) {
let cachedState = store.getState()[name];
return () => {
const playerState = store.getState()[name];
if (playerState) {
if (playerState !== cachedState) {
console.log(`${name}: ${playerState.score}, ${playerState.consecutiveFailures} échecs en cours`);
}
cachedState = playerState;
}
}

const AliceListener = PlayerListener('Alice');
const BobListener = PlayerListener('Bob');

/**
* function which keeps a cache of the currentState, updated at the end of the
* @returns {Function}
*/
function crowd() {
const fallenPinsOnLastThrow = store.getState().fallenPins;
if (fallenPinsOnLastThrow) {
console.log('👏👏 audience applauses 👏👏\n');
} else {
console.log('😞😞\n');
let cachedState = store.getState();
return () => {
const nextState = store.getState();
if (nextState !== cachedState) {
const fallenPinsOnLastThrow = nextState.fallenPins;
if (fallenPinsOnLastThrow) {
console.log('👏👏 audience applauses 👏👏\n');
} else {
console.log('😞😞\n');
}
} else {
console.log('\t\t\t/!\\TOUTE L\'ASSISTANCE AVAIT-ELLE LES YEUX FERMÉS ??');
}
cachedState = nextState;
}
}

// store.subscribe returns a function to unregister the listener
const unsubscribeAliceListener = store.subscribe(AliceListener);
const unsubscribeBobListener = store.subscribe(BobListener);
store.subscribe(crowd);
store.subscribe(crowd());

store.dispatch(initGame(['Alice', 'Bob']));
store.dispatch(throwPin([12, 4, 6, 2], 'Alice'));
Expand Down
136 changes: 0 additions & 136 deletions talk/plugin/markdown/example.html

This file was deleted.

36 changes: 0 additions & 36 deletions talk/plugin/markdown/example.md

This file was deleted.

50 changes: 37 additions & 13 deletions talk/resources/content.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Redux
## bonnes et moins bonnes pratiques

~~~
## objectifs de la présentation
- introduire Redux
- mettre en lumière certaines pratiques
Note: en se concentrant sur Redux, et non sur le couple React + Redux
///
## les bases
![](resources/transistor-transparent.png) <!-- .element: class="slide-icon" -->
Expand All @@ -16,7 +23,7 @@ Note:

~~~
## 3 principes
1. single source of truth
1. _single source of truth_
1. _state_ en lecture seule
1. changements de _state_ par functions pure (_reducers_)
Expand Down Expand Up @@ -245,18 +252,14 @@ Note: on pourrait avoir comme listener un composant graphique, une fonction qui

///
## la meilleure solution de gestion d'état ?
![otis](resources/bonne-ou-mauvaise-solution.jpg)<!-- .element: class="fragment" data-fragment-index="1" -->
![otis](resources/bonne-ou-mauvaise-solution.jpg)<!-- .element: class="fragment" -->

Note: le but n'est pas de dire que que redux est mieux ou moins bien que telle ou telle solution de gestion d'état, elle a ses inconvénients et ses avantages ; elle reste néanmoins une librairie très utilisée et qu'il est bon de maîtriser pour l'exploiter au mieux


///
## objectif de la présentation
=> mettre en lumière certaines pratiques
~~~
### structuration du _state_
📄 <!-- .element: class="slide-icon" -->
- "normaliser" les données <!-- .element: class="fragment" data-fragment-index="1" -->
- <!-- .element: class="fragment" data-fragment-index="2" -->
~~duplication~~ <!-- .element: class="fragment" data-fragment-index="2" -->
Expand All @@ -269,7 +272,9 @@ Note:
- un changement de structure UI ne devrait pas changer la structure du _state_

~~~
### structuration du _state_, épisode II
### structuration du _state_
### épisode II
📄 <!-- .element: class="slide-icon" -->
#### ~~états imbriqués~~
- complexifient le reducer, et <!-- .element: class="fragment" -->
- recharger trop de composants puisqu'on met à jour tout le super-state en modifiant un sous-state <!-- .element: class="fragment" -->
Expand All @@ -294,7 +299,10 @@ Note:
TODO: faire un exemple (en pur JS + redux) avec des console.log dans les subscribers à plusieurs niveaux du state, puis normaliser le state et montrer la différence.

~~~
### structuration du _state_, épisode III
### structuration du _state_
### épisode III
📄 <!-- .element: class="slide-icon" -->
dictionnaire (hashmap&lt;id, value>) plutôt que tableau
exemple: liste de pays triée par population <!-- .element: class="fragment" -->
Expand Down Expand Up @@ -365,22 +373,38 @@ Note:
- regrouper au sein d'un fichier par périmètre fonctionnel reducer, types, et actionCreators.
- Export nommé pour les actionCreators et les sélecteurs, export par défaut du reducer

==> EN GARDANT BIEN A L'ESPRIT QUE...
### mapping action - reducer: 1-n
Une même action peut faire réagir plusieurs reducers. Exemple:
__Rappel:__ <!-- .element: class="fragment" data-fragment-index="1" -->
##### mapping action - reducer: 1-n <!-- .element: class="fragment" data-fragment-index="1" -->

Note: Une même action peut faire réagir plusieurs reducers. Exemple:
`dispatch({ type: COMMENT_SUBMIT ... });` peut être traité par
- commentReducer: qui va ajouter/modifier le commentaire
- uiReducer: qui va fermer le formulaire
- articleReducer: qui va mettre à jour la date de dernier commentaire

~~~
### NE PAS modifier un objet imbriqué du state
TODO: take examples from https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns
```javascript
function updateNestedState(state, action) {
let nestedState = state.nestedState;
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data;
return {
...state,
nestedState
};
}
```
Note:
modifier directement dans l'exemple molkky-vanilla
##### remèdes:
- rigueur, ou
- librairie garantissant l'absence de mutation du state, Immutable-js par exemple
Note:
examples from https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns
~~~
### Tests
Expand Down

0 comments on commit 52afafa

Please sign in to comment.