Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

React lecture

Lecture on React, Flux and Redux

  • Login to see the comments

React lecture

  1. 1. React & Redux Christoffer Noring Google Developer Expert
  2. 2. Contents React basics Flux Redux CSS Mixins Router Tooling and Best Practices covered by Fabrice and Mathieu
  3. 3. React Basics
  4. 4. React lib React.createClass({}) class Component extends React.Component
  5. 5. React DOM ReactDOM.render( [Component]/ [JSX Expression], [element] ) Render our app
  6. 6. 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
  7. 7. JSX Syntax 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> ); JSX const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' ); ES5 JSX is your friend, you don’t have to use it HTML in my javascript!!
  8. 8. Components ES5 : React.createClass({})
  9. 9. Your first component app.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('examp ReactDOM.render( ); <script type="text/babel" src="app.js"></script> replace with createClass(), creates the component
  10. 10. Your second component var 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
  11. 11. Component, input <Component prop={ data }> this.props.prop
  12. 12. Component with data let data = { name : 'chris', profession : 'software developer', skills : ['.net', 'javascript', 'angular', 'react'] } This is what we want to render looks 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”
  13. 13. Rendering a list var 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
  14. 14. Refactoring Putting 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> ); } })
  15. 15. We can do this even better var 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
  16. 16. Component tree so far App CV Skills Skill Skill Skill etc…
  17. 17. What about methods?
  18. 18. <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
  19. 19. State
  20. 20. 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>
  21. 21. Changing state var 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}); }
  22. 22. State summary getInitialState(){ return { prop : ‘’ } } { this.state.prop } this.setState({ prop : ‘newValue’ })
  23. 23. Lifecycle events Initial State Changes Props changes Unmounting
  24. 24. Initial getDefaultProps getInitialState componentWillMount render componentDidMount Set initial values access DOM fetch data
  25. 25. State changes shouldComponentUpdate componentWillUpdate render componentDidUpdate boolean : is rerendering needed prepare for update perform DOM operations
  26. 26. Props change componentWillReceiveProps shouldComponentUpdate componentWillUpdate render componentDidUpdate called only when props have changed perform DOM operations
  27. 27. Unmounting componentWillUnmount is called before component is removed from DOM do cleanup, think of it as a destructor
  28. 28. ES6
  29. 29. We want to write in ES6 cause it has nice features Object.assign() Let Const Spread Operator Lambdas ES6 modules import {} from ‘’ export default export { a,b,c }
  30. 30. ES6 component class 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
  31. 31. PropTypes
  32. 32. 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
  33. 33. Many validation types oneOfType oneOf arrayOf objectOf element instanceOf symbol custom Further reading, https://facebook.github.io/react/docs/ typechecking-with-proptypes.html
  34. 34. Flux Architecture pattern by Facebook
  35. 35. 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
  36. 36. Action Dispatcher Store React { type : ADD_TODO, todo : { title : ‘dfdfd’ } } What Payload
  37. 37. Dispatcher Action Dispatcher Store React Dispatches actions dispatch(action) Handles a dispatched action through register(function(action){})
  38. 38. Store Action Dispatcher Store React Contains your state, usually same file have ability to perform ajax and also notify listeners when state has been updated
  39. 39. 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
  40. 40. store.js var 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
  41. 41. 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
  42. 42. ActionCreator Actions = { addTodo : function(todo) { Dispatcher.dispatch( { actionType : ADD_TODO, data : todo } ) } … } Dispatch the action
  43. 43. Redux
  44. 44. Why Redux or Flux growing pains Scales well on larger complex apps Boiler plate is way too big on smaller apps
  45. 45. Data flow Action Store React Reducers No dispatcher!!
  46. 46. Action
  47. 47. Action Store React Reducers function addTodo( todo ) { return { type : ‘ADD_TODO’, todo: todo } } Action Creator Represent user intent Must have a type
  48. 48. Redux Store
  49. 49. store.dispatch( action ); store.subscribe( listener ); store.getState() replaceReducer( nextReducer ) Action Store React Reducers
  50. 50. Reducers
  51. 51. 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?
  52. 52. Change object Change Array
  53. 53. Change Object What about a large object hierarchy? Object.assign( target, oldObject, newState ) Object.assign( {}, { update : false, name : ‘’ }, { update : true } ) { update : true, name : ‘’ } Merged
  54. 54. 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 item Spread operator …
  55. 55. Reducer no-no list Mutate arguments Perform side effects Call non-pure functions
  56. 56. Benefits to immutable state Clarity 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
  57. 57. State Summary ES5 lodash 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
  58. 58. All Reducers are called on each dispatch addItemReducer removeItemReducer listItemsReducer Dispatching addItem { type: ADD_ITEM, : item : item } returns new state return state return state
  59. 59. Connect Redux to our App
  60. 60. react-redux Action Store React Reducers Provider, attaches app to store Connect, creates container items
  61. 61. Connect store data to our app ComponentApp Provider Store Data Setup our store Wrap App component in a Provider
  62. 62. 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
  63. 63. Initialise store import { 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
  64. 64. 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
  65. 65. Component Wrap our component in a container component using Connect Presentational Component Container component export default connect( mapStateToProps, mapDispatchToProps )( PresentationalComponent ) what state what functions
  66. 66. what state should I expose as props mapStateToProps mapStateToProps = () => { return { appState : ‘’, otherAppState : ‘’ } } // Component this.props.appState this.props.otherAppState Every time a change happens this function is rerun, so don’t do anything expensive in there
  67. 67. 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();
  68. 68. mapDispatchToProps - nicer what actions do we want to expose to the component mapDispatchToProps = () => { return { actions : bindActionCreators(actions, dispatch) } } This is a shorthand using Redux this.actions.methodName()
  69. 69. Container component Smart component Focus on how things work “Aware” of Redux Subscribe to Redux state Dispatch redux actions react-redux lib
  70. 70. Presentational component Normal component that show data through props, knows nothing about Redux Presentation component gets decorated
  71. 71. 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
  72. 72. Redux flow so far Actions 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
  73. 73. Redux concepts so far Container and Presentation components Has methods, Has state, Knows Redux Just props No Redux ReactRedux Provider Pass store data to Components Connect mapStateToProps Pass state and methods to Components mapDispatchToProps
  74. 74. Code Demo
  75. 75. Improving our Redux
  76. 76. 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()
  77. 77. 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
  78. 78. And a little more…
  79. 79. import { bindActionCreators } from ‘redux’ This function mapDispatchToProps(dispatch) { return addTodo : todo => dispatch(todoActions.addTodo(todo)) } Becomes this function mapDispatchToProps(dispatch) { return actions : bindActionCreators( todoActions, dispatch ) } MAGIC !! onSave(){ this.props.actions.addTodo(this.state.todo)); }
  80. 80. Clean up magic strings
  81. 81. export default { ADD_TODO : 'ADD_TODO' } actionTypes.js todo-actions.js import actionTypes from './action-types'; function addTodo(todo) { return { type : actionTypes.ADD_TODO, todo : todo }; } todo-reducer.js export 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
  82. 82. Async load
  83. 83. 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.js const store = configureStore(); store.dispatch( loadTodos() ) set initial data Call dispatch when Ajax is done
  84. 84. export default function todoReducer( state = [], action) { switch(action.type) { … case actionTypes.LOAD_TODOS : return action.todos default : return state; } } todo-reducer.js
  85. 85. Component class 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);
  86. 86. Async load summary Create 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
  87. 87. Thank you

×