87
React & Redux Christoffer Noring Google Developer Expert

React lecture

Embed Size (px)

Citation preview

Page 1: React lecture

React & ReduxChristoffer Noring

Google Developer Expert

Page 2: React lecture

ContentsReact basicsFluxReduxCSSMixinsRouter

Tooling and Best Practices covered by Fabrice and Mathieu

Page 3: React lecture

React Basics

Page 4: React lecture

React lib

React.createClass({})

class Component extends React.Component

Page 5: React lecture

React DOM

ReactDOM.render( [Component]/ [JSX Expression], [element] )

Render our app

Page 6: React lecture

Minimum Setup ES5<script src="node_modules/react/dist/react.js"></script> <script src="node_modules/react-dom/dist/react-dom.js"></script> <script src=“https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>

<body> <div id="example"></div> <script type="text/babel"> ReactDOM.render( ); </script> </body>

<h1>Hello, world!</h1>,document.getElementById('example')

Where to render the appContent

Page 7: React lecture

JSXSyntax extension to javascript

const element = <h1>Hello, world!</h1>;

Allows us to do things like:

Needs to be transpiled :

const element = ( <h1 className="greeting"> Hello, world! </h1> );

JSXconst element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' );

ES5

JSX is your friend, you don’t have to use it

HTML in my javascript!!

Page 8: React lecture

ComponentsES5 : React.createClass({})

Page 9: React lecture

Your first componentapp.js index.html

var App = React.createClass({ render: function() { return ( <div className="app"> Hello, world! I am an app component </div> ); } });

ReactDOM.render( <App />, document.getElementById('example') );

render() is like a paint method

<body> <div id="example"></div> <script type="text/babel">

</script> </body>

<h1>Hello, world!</h1>,document.getElementById('example')

ReactDOM.render( );

<script type="text/babel" src="app.js"></script>

replace with

createClass(), creates the component

Page 10: React lecture

Your second componentvar CV = React.createClass({ render: function() { return ( <div className="comments"> Showing your cv </div> ); } });

var App = React.createClass({ render: function() { return ( <CV /> ); } });

Component within a component = component centric programming

Call createClass() again

return CV component from App component

Page 11: React lecture

Component, input<Component prop={ data }>

this.props.prop

Page 12: React lecture

Component with datalet data = { name : 'chris', profession : 'software developer', skills : ['.net', 'javascript', 'angular', 'react'] }

This is what we want to renderlooks like a property

looks like a list

ReactDOM.render( <App data={ data } />, document.getElementById('example') );

property = { variable }

Access data inside component like this:var App = React.createClass({ render: function() { return ( <div> { this.props.data.name } </div> ); } });

this.props.data.name“chris”

Page 13: React lecture

Rendering a listvar App = React.createClass({ render: function() {

return ( <div> <div> { this.props.data.name } </div> <div>{ skills }</div> </div> ); } });

let skills = this.props.data.skills.map(skill => { return ( <div className="skill">{skill}</div> ); });

Projection

Interpolation

Everything is in the “App” component = BAD

Page 14: React lecture

RefactoringPutting everything in the App component isn’t very “react like” of us

var App = React.createClass({ render: function() { return ( <CV data={ this.props.data } /> ); } });

var CV = React.createClass({ render: function() { return ( <div className="cv"> { this.data.props.name } <Skills data={ this.data.props.skills } > </div> ); } });

var Skills = React.createClass({ render : function(){ var skills = this.props.data.map(function(skill) { return ( <div className="skill">{skill}</div> ); }); return( <div className="skills"> <h2>Skills</h2> { skills } </div> ); } })

Page 15: React lecture

We can do this even bettervar Skills = React.createClass({ render : function(){ var skills = this.props.data.map(function(skill) { return (

<Skill data={ skill } /> ); }); return( <div className="skills"> <h2>Skills</h2> { skills } </div> ); } })

var Skill = React.createClass({ render : function(){ return( <div className="skills"> <h3>{ this.props.data }</h3> </div> ); } })

A component should do one thing well

One Skill comp per skill item

Page 16: React lecture

Component tree so farApp

CV

Skills

Skill

Skill

Skill

etc…

Page 17: React lecture

What about methods?

Page 18: React lecture

<button type="submit" onClick={this.onSubmit} > Save new skills </button>

var CV = React.createClass({ onSubmit : function(e){ e.preventDefault(); // do stuff }, render : function(){ return (

) }

Add method to our object literal

Refer to method in markup

Page 19: React lecture

State

Page 20: React lecture

Component State

var App = React.createClass({ getInitialState : function() { return { a : ‘some state’ }; }, render : function() { return <div>{ this.state.a }</div> }

});

Reading state getInitialState is read once per bootstrap,

define your state here

this.state.<prop>

Page 21: React lecture

Changing statevar App = React.createClass({

getInitialState : function() { return { newSkill : ‘some state’, b : ‘some other state’ }; }, render : function() { return <div> <input value={this.state.newSkill} > { this.state.a } </div> }

});

For every change of input field update state

onChange={this.onSkillChange}

bind to onchange

onSkillChange : function(e){ this.setState({newSkill : e.target.value}); }

Page 22: React lecture

State summarygetInitialState(){ return { prop : ‘’ } }

{ this.state.prop }

this.setState({ prop : ‘newValue’ })

Page 23: React lecture

Lifecycle events

Initial State Changes Props changes

Unmounting

Page 24: React lecture

InitialgetDefaultProps

getInitialState

componentWillMount

render

componentDidMount

Set initial values

access DOMfetch data

Page 25: React lecture

State changesshouldComponentUpdate

componentWillUpdate

render

componentDidUpdate

boolean : is rerendering needed

prepare for update

perform DOM operations

Page 26: React lecture

Props changecomponentWillReceiveProps

shouldComponentUpdate

componentWillUpdate

render

componentDidUpdate

called only when props have changed

perform DOM operations

Page 27: React lecture

Unmounting

componentWillUnmount

is called before component is removed from DOM

do cleanup, think of it as a destructor

Page 28: React lecture

ES6

Page 29: React lecture

We want to write in ES6 cause it has nice features

Object.assign() Let ConstSpread Operator Lambdas

ES6 modules

import {} from ‘’export default

export { a,b,c }

Page 30: React lecture

ES6 componentclass Todo extends React.Component { constructor(){ super(); this.action = this.action.bind(this); this.state.prop = value }

render() { return ( <div onClick={this.action}>{this.props.title}</div> ) }

action(e){ this.props.click( this.props.id ); } }

GOTCHA, we need to call the following, for our methods to be picked up

We inherit from React.Component instead of

calling React.createClass({})

Otherwise its business as usual

we DON’t use getInitialState(){} we just set this.state in constructor

We create a proper class rather than an object literal

Page 31: React lecture

PropTypes

Page 32: React lecture

Catch bugs with type checking

Will give an error in a tool, wrong type

Component.propTypes ={ title : React.PropTypes.string }

<Component title=1 >

Example 1 - wrong type

Component.propTypes ={ title : React.PropTypes.string.isRequired }

Example 2 - required

<Component > Will giver error, prop missing

Page 33: React lecture

Many validation types

oneOfType oneOf

arrayOfobjectOf

element instanceOf

symbolcustom

Further reading, https://facebook.github.io/react/docs/typechecking-with-proptypes.html

Page 34: React lecture

FluxArchitecture pattern by Facebook

Page 35: React lecture

Action describes what should happen with what data

Action

Dispatcher

Store

React e.g. Add todo

Store notifies listener that data has changed and needs to be reread

Dispatch action to store, tell the store

Unidirectional flow, everything flows in one direction

Page 36: React lecture

Action

Dispatcher

Store

React

{ type : ADD_TODO, todo : { title : ‘dfdfd’ } }

What Payload

Page 37: React lecture

DispatcherAction

Dispatcher

Store

React

Dispatches actions dispatch(action) Handles a dispatched action through register(function(action){})

Page 38: React lecture

StoreAction

Dispatcher

Store

React

Contains your state, usually same file have ability to perform ajax and also notify listeners when state has been updated

Page 39: React lecture

var Todos = React.createClass({ getInitialState : function(){ return { todos : Store.getTodos() } } render : function(){ return ([ render todos ]) },

})

//todos-component.js

componentDidMount : function(){ Store.addChangeListener( this._onChange ) }, componentWillUnmount : function(){ Store.removeChangeListener( this._onChange ) }, onChange() { this.setState({ todos : Store.getTodos() }) }

Get data

1

Update state so render() is called

2

Page 40: React lecture

store.jsvar todos = []; function loadTodos(){ return todos; }

var Store = merge(EventEmitter.prototype, { getTodos : function(){ return todos; } emitChange : function(){ emit(‘change’) }, addChangeListener : function(callback){ this.on(‘change’, callback) }, removeChangeListener : function(callback) { this.removeListener(‘change’, callback) } })

Dispatcher.register(function(payload){ var action = payload.action; switch(action) { case ADD_TODO: todos.push( payload.data ) case LOAD_TODOS: loadTodos(); } Store.emitChange(); })

Calls _onChange() on the component

Called when Dispatcher.dispatch()

getData addListener removeListener notifyChange

Page 41: React lecture

var AddTodo = React.createClass({ render : function(){ return ([ todo input ]) }, createTodo : function(todo){ Actions.addTodo( todo ) } })

//add-todo-component.js

Calls the ActionCreator with a type and a payload

Page 42: React lecture

ActionCreatorActions = { addTodo : function(todo) { Dispatcher.dispatch( { actionType : ADD_TODO, data : todo } ) } … }

Dispatch the action

Page 43: React lecture

Redux

Page 44: React lecture

Why Redux or Flux growing pains

Scales well on larger complex apps

Boiler plate is way too big on smaller apps

Page 45: React lecture

Data flow

Action

Store

React

Reducers

No dispatcher!!

Page 46: React lecture

Action

Page 47: React lecture

Action

Store

React

Reducers

function addTodo( todo ) { return { type : ‘ADD_TODO’, todo: todo } }

Action Creator

Represent user intent

Must have a type

Page 48: React lecture

Redux Store

Page 49: React lecture

store.dispatch( action );

store.subscribe( listener );

store.getState()replaceReducer( nextReducer )

Action

Store

React

Reducers

Page 50: React lecture

Reducers

Page 51: React lecture

Action

Store

React

Reducers

function reducer(state, action) { return newState; }

(state, action) => state

Must be pure

Multiple reducers per app

We want to change the state, without mutating so how?

Page 52: React lecture

Change object

Change Array

Page 53: React lecture

Change ObjectWhat about a large object hierarchy?

Object.assign( target, oldObject, newState )

Object.assign( {}, { update : false, name : ‘’ }, { update : true })

{ update : true, name : ‘’ }

Merged

Page 54: React lecture

Changing array//mutable state = [ { id : 1, name : 'tomato' } ]

state.push( { id: 2, name : 'cucumber' } );

return state;

Mutating

Point of mutation

//immutable state = [ { id : 1, name : 'tomato' } ]

return [ ...state, Object.assign({}, state, course) ]

Immutable

old array+

new itemSpread operator …

Page 55: React lecture

Reducer no-no listMutate arguments

Perform side effects

Call non-pure functions

Page 56: React lecture

Benefits to immutable stateClarity answers - who changed that state?

Mutable, anyone could have changed it

Immutable, only a reducer could have changed it

Performance

No need to check every single property on an object

if(oldState != newState), reference check

Time travel debugging

Page 57: React lecture

State Summary

ES5lodash merge

lodash extend

Object-assign ( NPM )

Reference checking is super fast

Enables time-travel debugging through browser plugin

Object.assign()

… spread operator for arrays

ES6

Change state by

Page 58: React lecture

All Reducers are called on each dispatch

addItemReducer

removeItemReducer

listItemsReducer

Dispatching addItem{ type: ADD_ITEM, : item : item }

returns new state

return state

return state

Page 59: React lecture

Connect Redux to our App

Page 60: React lecture

react-redux

Action

Store

React

Reducers

Provider, attaches app to store

Connect, creates container items

Page 61: React lecture

Connect store data to our app

ComponentApp

ProviderStore Data

Setup our store

Wrap App component in a Provider

Page 62: React lecture

import { combineReducers } from 'redux'; import todos from './todo-reducer';

const rootReducer = combineReducers({ todos : todos}) // OR todos only, LHS is implied

export default rootReducer;

reducers/index.js

Root reducer

Combines all reducers in one, this is one we feed to the store

Page 63: React lecture

Initialise storeimport { createStore, applyMiddleware } from 'redux'; import rootReducer from '../reducers' // index.js import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';

export default function configureStore(initialState) { return createStore( rootReducer, initialState, applyMiddleware( reduxImmutableStateInvariant() ) ) }

configure-store.js

Create store give it a

root reducer initial state

and add middleware

Page 64: React lecture

Provider

<Provider store={store} > <App /> </Provider>

Uses Reacts context - don’t touch

import configureStore from './store/configureStore'; import { Provider } from 'ReactRedux';

Make your store available to all your components

Page 65: React lecture

Component

Wrap our component in a container component using

Connect

Presentational Component

Container component

export default connect( mapStateToProps, mapDispatchToProps )( PresentationalComponent )

what statewhat functions

Page 66: React lecture

what state should I expose as props

mapStateToProps

mapStateToProps = () => { return { appState : ‘’, otherAppState : ‘’ } }

// Componentthis.props.appStatethis.props.otherAppState

Every time a change happens this function is rerun, so don’t do anything expensive in there

Page 67: React lecture

mapDispatchToProps cont..mapDispatchToProps = (dispatch) { return { loadTodos : () => { dispatch( loadTodos() ); }, addTodo : (todo) => { dispatch( addTodo(todo) ); } } }

Manual approach, but clear whats happening, recommended when starting out

e.g. this.props.loadTodos();

Page 68: React lecture

mapDispatchToProps - nicerwhat actions do we want to expose to the component

mapDispatchToProps = () => { return { actions : bindActionCreators(actions, dispatch) } }

This is a shorthand using Redux

this.actions.methodName()

Page 69: React lecture

Container componentSmart component

Focus on how things work

“Aware” of Redux

Subscribe to Redux state

Dispatch redux actions

react-redux lib

Page 70: React lecture

Presentational componentNormal component that show data through props, knows nothing about Redux

Presentation component gets decorated

Page 71: React lecture

class TodoComponent extends React.Component{ onSave(){ this.props.dispatch(todoActions.addTodo(this.state.todo)); //console.log(this.state.todo.title); } }

function mapStateToProps(state, ownProps) { return { todos : state.todos } }

export default connect( mapStateToProps) (TodoComponent); 1

2

3

Reduxify your component in 3 steps

Decorate our Component

Expose state

Calling and dispatching an action

Page 72: React lecture

Redux flow so farActions Dispatches an action

Reducers current state + action = new state

Store let connected components know of state change

ReactRedux determine wether to tell React to update UI

React new data passed through props

YES

Something happenedReact

Page 73: React lecture

Redux concepts so farContainer and Presentation components

Has methods, Has state, Knows Redux

Just props No Redux

ReactReduxProvider Pass store data to ComponentsConnect

mapStateToProps Pass state and methods to ComponentsmapDispatchToProps

Page 74: React lecture

Code Demo

Page 75: React lecture

Improving our Redux

Page 76: React lecture

Change our call to .connect()export default connect( mapStateToProps) (TodoComponent);

export default connect( mapStateToProps mapDispatchToProps) (TodoComponent);

function mapDispatchToProps(dispatch) { return addTodo : todo => dispatch(todoActions.addTodo(todo)) }

Wrap the action in a dispatch()

Page 77: React lecture

onSave(){ this.props.dispatch(todoActions.addTodo(this.state.todo)); }

Change to

onSave(){ this.props.addTodo(this.state.todo)); }

All knowledge of dispatch is removed Much cleaner

Dispatch() is no longer injected into the component

Fixing the component

Page 78: React lecture

And a little more…

Page 79: React lecture

import { bindActionCreators } from ‘redux’

This

function mapDispatchToProps(dispatch) { return addTodo : todo => dispatch(todoActions.addTodo(todo)) }

Becomes thisfunction mapDispatchToProps(dispatch) { return actions : bindActionCreators( todoActions, dispatch ) }

MAGIC !!onSave(){ this.props.actions.addTodo(this.state.todo)); }

Page 80: React lecture

Clean up magic strings

Page 81: React lecture

export default { ADD_TODO : 'ADD_TODO' }

actionTypes.js

todo-actions.jsimport actionTypes from './action-types';

function addTodo(todo) { return { type : actionTypes.ADD_TODO, todo : todo }; }

todo-reducer.jsexport default function todoReducer(state = [], action) { switch(action.type) { case actionTypes.ADD_TODO: return [ ...state, Object.assign({}, action.todo) ]

And so on…

Replace string with constant

Page 82: React lecture

Async load

Page 83: React lecture

export function loadTodos() { return function(dispatch) { return service.getTodos().then( todos => { dispatch( loadTodosSuccess(todos) ) }).catch(error => { console.error(error) }) } }

export function loadTodosSuccess(todos) { return { type : types.LOAD_TODOS, todos : todos } }

todo-actions.js

index.jsconst store = configureStore();

store.dispatch( loadTodos() )

set initial data

Call dispatch when Ajax is done

Page 84: React lecture

export default function todoReducer( state = [], action) { switch(action.type) { … case actionTypes.LOAD_TODOS : return action.todos default : return state; } }

todo-reducer.js

Page 85: React lecture

Componentclass TodoComponent extends React.Component{ render(){ let todos = this.state.todos.map( todo => { return <div>{ todo }</div> }) return ( <div>{ todos }</div> ) } }

function mapStateToProps(state, ownProps) { return { todos : state.todos } }

export default connect( mapStateToProps) (TodoComponent);

Page 86: React lecture

Async load summaryCreate async action that calls dispatch when done

Add reducer for loading data

Add said reducer to rootReducer

store.dispatch( loadAction() ) in index.js

ensure its exposed in mapStateToProps

use in presentation component

Page 87: React lecture

Thank you