Redux, Rematch, et plus si affinités

A presentation at ReactNantes : meetup 4 in November 2018 in Nantes, France by Julien Tanguy

Slide 1

Slide 1

Redux, Rematch, et plus si affinités 1/63

Slide 2

Slide 2

@jutanguy

Slide 3

Slide 3

https://www.welcometothejungle.co/companies/valwin/jobs

Slide 4

Slide 4

(Progressive) Web App Profil utilisateur Objets métier

Slide 5

Slide 5

ProgressiveBig Web App

Slide 6

Slide 6

“Local state is fine” – Dan Abramov

Slide 7

Slide 7

Local state #NoRedux

Slide 8

Slide 8

Use the Context, Luke

Slide 9

Slide 9

Limites du state local

Slide 10

Slide 10

https://redux.js.org

Slide 11

Slide 11

Les trois principes de Redux Unique source de vérité État en lecture seule Les changements sont faits avec des fonctions pures Concepts fondamentaux Actions Reducers Store

Slide 12

Slide 12

Action Objet avec une clé type, décrivant un ou des changements passés.

Slide 13

Slide 13

Action creator

  1. export const addTodo = (text) => ({ type: 'TODO_ADDED', id: newUUID(), text: text }) Fonction qui retourne une action.

Slide 14

Slide 14

Reducer Fonction permettant d'aggréger ce qui s'est passé dans un état

Slide 15

Slide 15

Reducer

Slide 16

Slide 16

Store Encapsule l'état Fournit les actions pour interroger l'état et dispatcher les actions

Slide 17

Slide 17

Store

import { createStore } from 'redux'; import todoReducer, { addTodo } from './reducers'; const store = createStore(todoReducer, window.STATE_FROM_SERVER) console.log(store.getState()) store.dispatch(addTodo('Tester redux')) console.log(store.getState())

Slide 18

Slide 18

Store 1 2 3 4 5 6 7 const store = createStore(todoReducer) console.log(store.getState()) store.dispatch(addTodo('Tester redux')) console.log(store.getState()) Powered by RunKit Node 10  help  run

Slide 19

Slide 19

Middleware

Slide 20

Slide 20

Logger Middleware 1 2 3 4 5 6 7 8 9 10 11 12 const logger = store => next => action => { console.group(action.type); console.log('action: ', action); const result = next(action); console.log('next state: ', store.getState()); console.groupEnd(); return result; } const store = createStore(todoReducer, applyMiddleware(logger)) store.dispatch(addTodo('Tester redux')) Powered by RunKit Node 10  help  run

Slide 21

Slide 21

Utilisation avec react

import import import import import React from 'react' { render } from 'react-dom' { Provider } from 'react-redux' createStore from './store' App from './components/App' const store = createStore(); render( <Provider store={store}> <App />

Slide 22

Slide 22

app.js

Slide 23

Slide 23

New Devtools Redux > Simple

Slide 24

Slide 24

Redux recap

Slide 25

Slide 25

Slide 26

Slide 26

“You might not need Redux” – Dan Abramov

Slide 27

Slide 27

Redux === Code applicatif

Slide 28

Slide 28

Redux & Tests Meetup 2: “Ceintures, bretelles et stratégies de test” par Sylvain Bannier

Slide 29

Slide 29

Actions creators & Tests

Slide 30

Slide 30

Reducers & Tests

  1. import reducer, { addTodo } from './reducers'; 2. 3. describe('reducer', () => { 4. it('comes with an initial state', () => { 5. expect(reducer(undefined)).toMatchSnapshot(); 6. }); 7. it('should apply actions', () => { 8. const newState = reducer(undefined, addTodo("Tester le reducer")); 9. expect(newState).toHaveLength(1); 10. }); 11. });

Slide 31

Slide 31

Reducers & Tests 2

  1. import reducer, { addTodo } from './reducers'; 2. import diff from 'snapshot-diff'; 3. 4. 5. describe('reducer', () => { 6. it('comes with an initial state', () => { 7. expect(reducer(undefined)).toMatchSnapshot(); 8. }); 9. it('should apply actions', () => { 10. const initialState = reducer(undefined); 11. const newState = reducer(initialState, addTodo("Tester le reducer")); 12. expect(diff(initialState, newState)).toMatchSnapshot(); 13 })

Slide 32

Slide 32

Gestion des effets Comment faire des appels à des APIs ?

Slide 33

Slide 33

utilisation de redux-thunk

Slide 34

Slide 34

redux-thunk

                        1. 13 function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } } }; return next(action); const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware;

Slide 35

Slide 35

redux-saga Je n'ai toujours pas saisi les générateurs en js

Slide 36

Slide 36

redux-api-middleware

Slide 37

Slide 37

  1. import { RSAA } from redux-api-middleware; // RSAA = '@@redux-api-middleware/RSAA' 2. 3. { 4. [RSAA]: { 5. endpoint: 'http://www.example.com/api/users', 6. method: 'GET', 7. types: ['REQUEST', 'SUCCESS', 'FAILURE'] 8. } 9. } 10.

Slide 38

Slide 38

Request

  1. { 2. "type": "REQUEST" 3. }

Slide 39

Slide 39

Response

  1. { 2. "type": "SUCCESS", 3. "payload": { 4. "users": [ 5. { "id": 1, "name": "John Doe" }, 6. { "id": 2, "name": "Jane Doe" }, 7. ] 8. } 9. }

Slide 40

Slide 40

Problèmes avec redux-api-middleware On perd le contrôle du flot d'actions Comment faire

Slide 41

Slide 41

Ma préférence: redux-thunk

Slide 42

Slide 42

Advanced Redux

Slide 43

Slide 43

Normalisation de l'état

Slide 44

Slide 44

Response

Slide 45

Slide 45

Normalizr

import { normalize, schema } from 'normalizr'; const user = new schema.Entity('users'); const comment = new schema.Entity('comments', { commenter: user, }); const article = new schema.Entity('articles', { author: user, comments: [comment],

Slide 46

Slide 46

1 2 3 4 5 6 7 8 9 10 11 12 const user = new schema.Entity('users'); const comment = new schema.Entity('comments', { commenter: user, }); const article = new schema.Entity('articles', { author: user, comments: [comment], }); const normalizedData = normalize(data, article); Powered by RunKit Node 10  help  run

Slide 47

Slide 47

Architecture #protip La distinction entre container et component n'est pas forcément utile à refléter dans le filesystem

Slide 48

Slide 48

Convention de nommage https://hackernoon.com/structuring-projects-and-naming-componentsin-react-1261b6e18d76

Slide 49

Slide 49

Better naming

src ├─ components │ ├─ User │ │ ├─ Form │ │ │ ├─ Form.jsx │ │ │ └─ Form.css │ │ └─ List.jsx │ └─ UI │ └─ screens ├─ User

Slide 50

Slide 50

Les gros fichiers ne sont pas nécessairement mauvais

Slide 51

Slide 51

Dan Abramov @dan_abramov Okay, I give in. I wrote a guide on the most scalable file structure for React projects. I’m using it every day. Best part: it works for Vue projects too. Hope it’s helpful! react-filestructure.surge.sh 5,268 6:30 PM - Aug 8, 2018 1,764 people are talking about this

Slide 52

Slide 52

move files around until it feels right

Slide 53

Slide 53

Frederic Barthelemy @fbartho · Aug 8, 2018 Replying to @dan_abramov I think beginners don’t know how anything feels, and want a guideline for where to start so they can focus on harder concepts, faster. (* me as a React beginner 8 months ago looking at those same articles) Dan Abramov @dan_abramov It is a guideline though. It means literally “start by putting everything in one file; when it feels like it’s annoying, start splitting them up; what THAT gets annoying, maybe add some folders”. 461 6:43 PM - Aug 8, 2018 95 people are talking about this

Slide 54

Slide 54

Redux ducks

Slide 55

Slide 55

redux/modules/todo.js

Slide 56

Slide 56

Redux est verbeux C'est normal, il n'a pas été conçu pour être concis Best practices Boilerplates

Slide 57

Slide 57

Rematch

Slide 58

Slide 58

Rematch = Redux + Best practices https://hackernoon.com/redesigning-redux-b2baee8b8a38 redéclaration des types d'actions action creators thunks création du store mapDispatchToProps

Slide 59

Slide 59

redux duck

                                    1. 19 import newUUID from 'uuid/v4'; // Action types const ADDED = 'TODO_ADDED'; // Reducer export default function reducer(state = [], action){ switch(action.type){ case 'TODO_ADDED': return [...state, {id: action.id, text: action.text}]; default: return state; } } // Action creators export const todoAdded = (text, id) => ({ type: ADDED, id: id, text: text }); export const addTodo = (text) => dispatch => axios

Slide 60

Slide 60

Rematch

Slide 61

Slide 61

New Devtools Redux > Rematch

Slide 62

Slide 62

Merci https://www.welcometothejungle.co/companies/valwin/jobs

Slide 63

Slide 63

Liens https://redux.js.org https://rematch.gitbooks.io/rematch