Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now...

135
Managing Data with React and Redux 1

Transcript of Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now...

Page 1: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Managing Data with React and Redux

1

Page 2: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

@Jack_Franklin@pusher

2

Page 3: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

• Code, notes, etc: github.com/jackfranklin/react-redux-talk

• Slides (after talk): speakerdeck.com/jackfranklin

• I'll tweet them all: twitter.com/jack_franklin

3

Page 4: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

4

Page 5: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

5

Page 6: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

In the beginning

6

Page 7: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

app/todos.js

export default class Todos extends React.Component {

constructor(props) {

super(props);

this.state = {

todos: [

{ id: 1, name: 'Write the blog post', done: false },

{ id: 2, name: 'Buy Christmas presents', done: false },

{ id: 3, name: 'Leave Santa his mince pies', done: false },

]

}

}

...

}

7

Page 8: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

app/todos.js

render() {

return (

<div>

<p>The <em>best</em> todo app out there.</p>

<h1>Things to get done:</h1>

<ul className="todos-list">{ this.renderTodos() }</ul>

<AddTodo onNewTodo={(todo) => this.addTodo(todo)} />

</div>

)

}

8

Page 9: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

<AddTodo onNewTodo={(todo) => this.addTodo(todo)} />

9

Page 10: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Parent component contains all state.

Child components are given functions to call to tell the parent component of the new state.

10

Page 11: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

app/todos.js contained the logic for updating the state from some user input.

constructor(props) {...}

addTodo(todo) {

const newTodos = this.state.todos.concat([todo]);

this.setState({ todos: newTodos });

}

...

render() {...}

11

Page 12: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

But then as this component grew I pulled out the business logic into standalone JavaScript functions:

12

Page 13: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

app/todos.js

constructor(props) {...}

addTodo(todo) {

this.setState(addTodo(this.state, todo));

}

...

render() {...}

13

Page 14: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

State functions can take the current state and produce a new state.

export function deleteTodo(state, id) {

return {

todos: state.todos.filter((todo) => todo.id !== id)

};

}

14

Page 15: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

This is effectively a very, very basic Redux (but worse in many ways!).

15

Page 16: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

This is fine for small applications, but it tightly couples components and makes refactoring or restructuring components trick and makes refactoring or restructuring components tricky.

16

Page 17: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

The more data you have, the more difficult it is to manage as different components can edit different pieces of data.

17

Page 18: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

If you split the data up across components, you no longer have a single source of truth for your application's data.

18

Page 19: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

It's tricky to track down what caused the data to change, and where it happened.

grep setState

19

Page 20: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

As your application grows you need some process and structure around your data.

20

Page 21: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

But don't use Redux by default! For smaller apps you'll probably find yourself quite content without.

21

Page 22: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Redux

22

Page 23: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

The three principles of Redux.• Single Source of Truth: all data is stored in

one object.

• State is read only: nothing can directly mutate the state.

• The state is manipulated by pure functions: no external data can affect them.

23

Page 24: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Building a Redux application

24

Page 25: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

import { createStore } from 'redux';

function counter(state, action) {

...

}

const store = createStore(counter);

console.log('Current state', store.getState());

25

Page 26: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

• store: the object that holds our state

• action: an object sent to the store in order to manipulate the store's data

• reducer: a function that takes the current state, an action and produces the new state.

26

Page 27: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

First: define your actions.

{ type: 'INCREMENT' }

{ type: 'DECREMENT' }

27

Page 28: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Second: define how your reducer should deal with those actions:

function counter(state, action) {

switch (action.type) {

case 'INCREMENT':

return state + 1;

case 'DECREMENT':

return state - 1;

default:

return state;

}

}

28

Page 29: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Third: define what to do if you don't have any state:

function counter(state, action) { if (!state) state = 0; ...}

// ORfunction counter(state = 0, action) { ...}

29

Page 30: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Fourth: create your store and dispatch some actions:

const store = createStore(counter);

store.dispatch({ type: 'INCREMENT' });

console.log('Current state', store.getState());

// => 1

30

Page 31: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

What makes this good?• The main logic is contained in a function,

abstracted away from the store. It's easy to modify, follow and test.

• Nothing ever manipulates the state, all manipulation is done via actions.

• Actions are just plain objects; they can be logged, serialised, repeated, and so on.

• Our reducer is pure - the state is never 31

Page 32: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Adding Redux to a React applicationnpm install --save redux react-redux

32

Page 33: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

First, let's decide what our state will look like:

{

todos: [

{ id: 1, name: 'buy milk', done: false },

...

]

}

33

Page 34: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Secondly, let's define the actions.

• { type: 'ADD_TODO', name: '...' }

• { type: 'DELETE_TODO', id: ... }

• { type: 'TOGGLE_TODO', id: ... }

34

Page 35: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Thirdly, let's define the reducer function that will deal with these actions.

export default function todoAppReducers(

state = { todos: [] },

action

) {

...

};

35

Page 36: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

We can first deal with ADD_TODO:

switch (action.type) { case 'ADD_TODO': const todo = { name: action.name, id: state.todos.length, done: false }

return { todos: state.todos.concat([todo]) }}

36

Page 37: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

And then DELETE_TODO:

case 'DELETE_TODO': return { todos: state.todos.filter((todo) => todo.id !== action.id) }

37

Page 38: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

And finally TOGGLE_TODO:

case 'TOGGLE_TODO': const todos = state.todos.map((todo) => { if (todo.id === action.id) { todo.done = !todo.done; }

return todo; });

return { todos };38

Page 39: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

We've just modelled most of our business logic without having to deal with UI interactions or anything else.

This is one of the biggest pluses to using Redux.

39

Page 40: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Now let's create a store and connect our components.

40

Page 41: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

By default a component does not have access to the store.

Components that do are known as "smart" components.

Components that do not are known as "dumb" components.

41

Page 42: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

app/index.js

import React from 'react';

import { render } from 'react-dom';

import Todos from './todos';

class AppComponent extends React.Component {

render() {

return <Todos />;

}

}

render(

<AppComponent />,

document.getElementById('app')

);

42

Page 43: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

We'll firstly create a store and connect our application.

// other imports skipped

import { Provider } from 'react-redux';

import { createStore } from 'redux';

import todoAppReducers from './reducers';

const store = createStore(todoAppReducers);

class AppComponent extends React.Component {...}

render(

<Provider store={store}>

<AppComponent />

</Provider>,

document.getElementById('app')

);

43

Page 44: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

All the Provider does is make components in our application able to connect to the store if we give them permission.

You only need to wrap your top level component in the Provider, and once it's done you can mostly forget about it.

44

Page 45: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

We now have a store and our top level component has been given access to it.

Now we need to give our components access to the store so they can render the data to it.

45

Page 46: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

The Todos component needs access to the todos in the store so it can render the individual todos.

We can use the connect function from react-redux to do this.

46

Page 47: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

app/todos.js

import { connect } from 'react-redux';

class Todos extends React.Components {...};

const ConnectedTodos = connect((state) => {

return {

todos: state.todos

}

})(Todos);

47

Page 48: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

The connect function takes a React component and produces a new component that will be given access to parts of the state as props.

This lets us strictly control which parts of our state each component has access to.

It also provides this.props.dispatch, which is used to dispatch actions, which we'll see later.

48

Page 49: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

So now within our Todos component we can swap this.state.todos to this.props.todos.

We can also get rid of all the functions for managing state, and stop passing them through to child components.

49

Page 50: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

class Todos extends React.Component {

renderTodos() {

return this.props.todos.map((todo) => {

return <li key={todo.id}><Todo todo={todo} /></li>;

});

}

render() {

return (

<div>

<ul className="todos-list">{ this.renderTodos() }</ul>

<AddTodo />

</div>

)

}

}

50

Page 51: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Notice how much cleaner this is, and how our component is purely focused on presentation.

51

Page 52: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Now let's hook up AddTodo so we can create new todos.

52

Page 53: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

AddTodo doesn't need to access any data in the store but it does need to dispatch actions, so it too must be connected.

class AddTodo extends React.Component {...};

const ConnectedAddTodo = connect()(AddTodo);

export default ConnectedAddTodo;

53

Page 54: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

The old addTodo method:

addTodo(e) {

e.preventDefault();

const newTodoName = this.refs.todoTitle.value;

if (newTodoName) {

this.props.onNewTodo({

name: newTodoName

});

this.refs.todoTitle.value = '';

}

}

54

Page 55: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

The new one:

addTodo(e) { e.preventDefault(); const newTodoName = this.refs.todoTitle.value; if (newTodoName) { this.props.dispatch({ name: newTodoName, type: 'ADD_TODO' }); ... }}

55

Page 56: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

And we're now using Redux to add Todos!

56

Page 57: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Finally, we can update the Todo component to dispatch the right actions for toggling and deleting.

57

Page 58: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

toggleDone() { this.props.dispatch({ type: 'TOGGLE_TODO', id: this.props.todo.id });}

deleteTodo(e) { this.props.dispatch({ type: 'DELETE_TODO', id: this.props.todo.id });}

58

Page 59: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

But there's a problem!

59

Page 60: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Mutation!

60

Page 61: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Redux expects you to never mutate anything, and if you do it can't always correctly keep your UI in sync with the state.

We've accidentally mutated...

61

Page 62: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

In our reducer...

case 'TOGGLE_TODO': const todos = state.todos.map((todo) => { if (todo.id === action.id) { todo.done = !todo.done; }

return todo; });

return { todos };62

Page 63: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

todo.done = !todo.done;

63

Page 64: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

A quick rewrite...

case 'TOGGLE_TODO': const todos = state.todos.map((todo) => { if (todo.id === action.id) { return { name: todo.name, id: todo.id, done: !todo.done } }

return todo; });

64

Page 65: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

And it all works, as does deleting a todo. We're fully Reduxed!

65

Page 66: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Deep breath...

66

Page 67: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

That probably felt like a lot of effort, but the good news is once you've set Redux up you are set.

67

Page 68: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

1.Decide the shape of your state.

2.Decide the actions that can update the state.

3.Define your reducers that deal with actions.

4.Wire up your UI to dispatch actions.

5.Connect your components to the store to allow them to render state.

68

Page 69: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Still to come1.Debugging Redux

2.Better Redux Reducers

3.Middlewares

4.Async actions

69

Page 70: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

70

Page 71: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

If you're not using Chrome you can still use the devtools but it takes a bit more effort.

See: https://github.com/gaearon/redux-devtools

71

Page 72: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Firstly, install the plugin in Chrome.

72

Page 73: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Secondly, update the call to createStore:

createStore(reducers, initialState, enhancer).

Enhancer: a function that enhances the store with middleware or additional functionality. We'll see this again later.

73

Page 74: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

const store = createStore(

todoAppReducers,

undefined,

window.devToolsExtension ? window.devToolsExtension() : undefined

);

We leave the initialState as undefined because our reducer deals with no state.

74

Page 75: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

75

Page 76: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

76

Page 77: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

77

Page 78: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Better Reducers

78

Page 79: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Imagine our TODO app now needs to have a user log in first, and our state will now keep track of the user that's logged in.

79

Page 80: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Our new state will look like:

{

todos: [

{ id: 1, name: 'buy milk', done: false },

...

],

user: {

id: 123,

name: 'Jack'

}

}

80

Page 81: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Next, let's define some new actions:

{ type: 'LOG_USER_IN', id: ..., name: '...' }{ type: 'LOG_USER_OUT' }

81

Page 82: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

And then our reducer needs to be updated. Firstly, now we have two keys in our state, we should update the reducers we have to not lose any keys they don't deal with.

82

Page 83: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Before:

case 'DELETE_TODO': return { todos: state.todos.filter((todo) => todo.id !== action.id) }

After:

case 'DELETE_TODO': return Object.assign({}, state, { todos: state.todos.filter((todo) => todo.id !== action.id) });

83

Page 84: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

And now we can add reducers for the new user actions:

case 'LOG_USER_IN': return Object.assign({}, state, { user: { id: action.id, name: action.name } });

case 'LOG_USER_OUT': return Object.assign({}, state, { user: {} });

84

Page 85: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

export default function todoAppReducers( state = { todos: [initialTodo], user: {} }, action) { switch (action.type) { case 'ADD_TODO': const todo = { name: action.name, id: state.todos.length, done: false }

return Object.assign({}, state, { todos: state.todos.concat([todo]) });

case 'DELETE_TODO': return Object.assign({}, state, { todos: state.todos.filter((todo) => todo.id !== action.id) });

case 'TOGGLE_TODO': const todos = state.todos.map((todo) => { if (todo.id === action.id) { return { name: todo.name, id: todo.id, done: !todo.done } }

return todo; });

return Object.assign({}, state, { todos });

case 'LOG_USER_IN': return Object.assign({}, state, { user: { id: action.id, name: action.name } });

case 'LOG_USER_OUT': return Object.assign({}, state, { user: {} });

default: return state;

}};

85

Page 86: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Our reducer is huge, and deals with two different data sets:

• User

• Todos

In a larger app this will quickly become impossible to manage.

86

Page 87: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Instead we split into multiple reducers who are each resopnsible for a specific key in the state.

Each of these reducers is only given their part of the state.

87

Page 88: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

function userReducer(user = {}, action) { switch (action.type) { case 'LOG_USER_IN': return { id: action.id, name: action.name } case 'LOG_USER_OUT': return {};

default: return user; }}

88

Page 89: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

function todoReducer(todos = [], action) { switch (action.type) { case 'ADD_TODO': ...

case 'DELETE_TODO': return todos.filter((todo) => todo.id !== action.id);

case 'TOGGLE_TODO': ...

default: return todos; }}

89

Page 90: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

And our main reducer function becomes:

export default function todoAppReducers(state = {}, action) {

return {

todos: todoReducer(state.todos, action),

user: userReducer(state.user, action)

}

};

90

Page 91: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Now as our state grows we'll add new functions for each key.

Turns out this pattern is so useful that Redux provides a method to do it for us: combineReducers.

91

Page 92: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Before:

export default function todoAppReducers(state = {}, action) {

return {

todos: todoReducer(state.todos, action),

user: userReducer(state.user, action)

}

};

92

Page 93: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

After:

import { combineReducers } from 'redux';

...

const todoAppReducers = combineReducers({

todos: todoReducer,

user: userReducer

});

export default todoAppReducers;

93

Page 94: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Deep breath again!

94

Page 95: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Middlewares

95

Page 96: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

You might be familiar with Rack Middlewares, NodeJS / Express middlewares, and so on.

It provides a third-party extension point between dispatching an action, and the moment it reaches the reducer

-- http://redux.js.org/docs/advanced/Middleware.html

96

Page 97: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

A middleware provides a function that is given the store object.

It should return another function that is called with next, the function it should call when it is finished.

That function should return another function that is called with the current action, action.

97

Page 98: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

WHAT?!

98

Page 99: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

const myMiddleware = function(store) {

return function(next) {

return function(action) {

// your logic goes here

// this is the dispatch function

// that each middleware can return

}

}

}

const myMiddleware = store => next => action => {

// your logic here

}

99

Page 100: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Each middleware replaces the dispatch function with its own function, and Redux chains them together correctly.

100

Page 101: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Example: logging each action

101

Page 102: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

app/middlewares.js

export const logMiddleware = store => next => action => {

console.log('MIDDLEWARE: About to dispatch', action);

return next(action);

};

102

Page 103: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

app/index.js

import { createStore, applyMiddleware } from 'redux';

...

const store = createStore(

todoAppReducers,

undefined,

applyMiddleware(logMiddleware)

);

103

Page 104: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

104

Page 105: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

But wait, we lost the devtools code!

105

Page 106: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

compose

106

Page 107: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

const store = createStore(

todoAppReducers,

undefined,

compose(

applyMiddleware(logMiddleware),

window.devToolsExtension ? window.devToolsExtension() : f => f

)

);

107

Page 108: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Final deep breath!

108

Page 109: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Async Actions

109

Page 110: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Up until now we've dealt purely with synchronous actions, but often your action will be async, most commonly, data fetching.

110

Page 111: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

We could write our own middleware or logic to deal with this, but one already exists: redux-thunk.

npm install --save redux-thunk

111

Page 112: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

With thunk, functions can either return an action or another function that can dispatch actions.

112

Page 113: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

The HTTP request cycle:

• Make the request, and dispatch action.

• Get the data back, and dispatch an action with that data.

• Request errors, dispatch an action with information about the error.

When you want to dispatch an action to cause an HTTP request you actually want to trigger multiple actions.

113

Page 114: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

export default function thunkMiddleware({ dispatch, getState }) {

return next => action => {

if (typeof action === 'function') {

return action(dispatch, getState);

}

return next(action);

};

}

114

Page 115: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Fake API:

export function fetchTodos() {

return new Promise((resolve, reject) => {

resolve({

todos: [{

id: 1,

name: 'Buy Milk',

done: false

}]

})

});

}

115

Page 116: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

We need some new information on the state:

{

todos: ...,

user: ...,

isFetching: true / false

}

116

Page 117: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

And some new actions:

{ type: 'REQUEST_TODOS_INIT' },{ type: 'REQUEST_TODOS_SUCCESS', todos: [...] }// and one for error handling// if this were real

117

Page 118: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

function isFetchingReducer(isFetching = false, action) { switch (action.type) { case 'REQUEST_TODOS_INIT': return true;

case 'REQUEST_TODOS_SUCCESS': return false

default: return isFetching }}

118

Page 119: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

const todoAppReducers = combineReducers({

todos: todoReducer,

user: userReducer,

isFetching: isFetchingReducer

});

119

Page 120: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Action Creatorsapp/action-creators.js

export function fetchTodosAction() {

return (dispatch) => {

}

}

120

Page 121: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

app/todos.js

import { fetchTodosAction } from './action-creators';

class Todos extends React.Component {

componentWillMount() {

this.props.dispatch(fetchTodosAction());

}

...

}

121

Page 122: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

First, dispatch REQUEST_TODOS_INIT:

export function fetchTodosAction() { return (dispatch) => { dispatch({ type: 'REQUEST_TODOS_INIT' }); }}

122

Page 123: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Then, fetch and dispatch REQUEST_TODOS_SUCCESS:

fetchTodos().then((data) => { dispatch({ type: 'REQUEST_TODOS_SUCCESS', todos: data.todos });});

123

Page 124: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

import { fetchTodos } from './fake-api';

export function fetchTodosAction() { return (dispatch) => { dispatch({ type: 'REQUEST_TODOS_INIT' });

fetchTodos().then((data) => { dispatch({ type: 'REQUEST_TODOS_SUCCESS', todos: data.todos }); }); }}

124

Page 125: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

app/todos.js

Within render:

{ this.props.isFetching && <p>LOADING...</p> }

Allow it access:

const ConnectedTodos = connect((state) => {

return {

todos: state.todos,

isFetching: state.isFetching

};

})(Todos);

125

Page 126: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Now we need our todosReducer to deal with the success action and use the data.

case 'REQUEST_TODOS_SUCCESS': return action.todos;

126

Page 127: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Et voila (the delay is me for effect!):

127

Page 128: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

And with that we now have support for async!

128

Page 129: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Housekeeping Actions

129

Page 130: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

The strings for our actions are cropping up in a lot of places.

Better to have them as constants that can be exported.

export const LOG_USER_IN = 'LOG_USER_IN';export const LOG_USER_OUT = 'LOG_USER_OUT';

That way you can keep things in sync easier.

130

Page 131: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Use action creators for creating actions:

export function addTodo(name) { return { type: ADD_TODO, name }};

131

Page 132: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

For more info: - http://redux.js.org/docs/basics/Actions.html- http://redux.js.org/docs/recipes/ReducingBoilerplate.html

132

Page 133: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

You've made it!Redux is tough to get started with but the benefits in a large application are huge.

• Code, notes, etc: github.com/jackfranklin/react-redux-talk

• Slides (after talk): speakerdeck.com/jackfranklin

• I'll tweet them all: twitter.com/jack_franklin

133

Page 134: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

Thanks :)

• @Jack_Franklin

• javascriptplayground.com

134

Page 135: Managing Data with React and Redux · This is one of the biggest pluses to using Redux. 39. Now let's create a store and connect our components. 40. By default a component does not

135