data handling in react: callbacks, flux, and redux

28
REACT CALLBACKS, FLUX AND REDUX

Upload: david-roberts

Post on 20-Mar-2017

1.928 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Data Handling in React: Callbacks, Flux, and Redux

REACT

CALLBACKS, FLUX AND REDUX

Page 2: Data Handling in React: Callbacks, Flux, and Redux

ABOUT ME

Page 3: Data Handling in React: Callbacks, Flux, and Redux

REACT INTRO

> React is a view library

Page 4: Data Handling in React: Callbacks, Flux, and Redux

A COMPONENT

var Comment = React.createClass({ render: function() { return ( <p> <span>Comment: </span> <span>{this.props.body}</span> </p> ) }});

var CommentList = React.createClass({ render: function() { return ( <div className="commentList"> <h1>Comments</h1> <Comment body="try React!" /> </div> ) }});

jsfiddle demo

Page 5: Data Handling in React: Callbacks, Flux, and Redux

REACT MYTHS

> MYTH: React requires ES6> TRUTH: ES5 will work fine> MYTH: I have to learn Flux

> TRUTH: There's no need to start with Flux and many other patterns can be used

instead

Page 6: Data Handling in React: Callbacks, Flux, and Redux

BUT WHAT ABOUT THE DATA?

This talk will cover 3 different patterns to handle data in React

Page 7: Data Handling in React: Callbacks, Flux, and Redux

CALLBACKS

> Best way to get started> Great for Hierarchical applications

> Great if few clientside changes

Page 8: Data Handling in React: Callbacks, Flux, and Redux

SHOW CALLS AND TEXT MESSAGES

<Application > <CallList> <Call id=1 /> <Call id=2 /> <Call id=3 /> </CallList> <TextMessageList> <TextMessage id=1 /> <TextMessage id=2 /> </TextMessage></Application>

> Top level Application component manages data> (read only) data is passed down component hierarchy

> See simple-callback branch

Page 9: Data Handling in React: Callbacks, Flux, and Redux

I WANT TO TEXT SOMEONE WHO CALLED ME

var Application = React.class({ ...

newText: function(textInfo) { this.setState({ messages: _.concat(this.state.messages, { id: this.state.messages.length + 1, from: textInfo.from, number: textInfo.number, status: "draft" }) }); }

render: function() { return ( <div> <CallList calls={this.state.calls} sendText={this.newText} /> <TextMessageList messages={this.state.messages} /> </div> ) }});

> This impacts two independent parts of the applicaiton> Must touch every component between Application and Call to provide a callback

> see text-from-call Pull Request

Page 10: Data Handling in React: Callbacks, Flux, and Redux

I WANT TO INTEGRATE THIS INTO A LARGER APPLICATION

<Application> <Directory> <Contact /> <Contact /> </Directory> <OtherImportantStuff /> <Communications > <CallList /> <TextMessageList /> </Communications ></Application>

> I didn't even attempt this one..

Page 11: Data Handling in React: Callbacks, Flux, and Redux

FLUX

> Flux is a pattern, NOT an implementation> Consistent way for data to flow throughout

the application

Page 12: Data Handling in React: Callbacks, Flux, and Redux

OVERVIEW OF FLUX

Page 13: Data Handling in React: Callbacks, Flux, and Redux

OVERVIEW OF FLUX

> One way flow> A single event is asynchronously dispatched

> Data only changed by the Store> Doesn't have to be for React

Page 14: Data Handling in React: Callbacks, Flux, and Redux

ACTION (CREATORS)

let actions = { addText(textInfo) { // example of error checking if ( textInfo.from ) { this.dispatch(constants.DRAFT_TEXT, textInfo) } },

fetchCalls() { var result = API.getCalls(); this.dispatch(constants.FETCH_CALLS, result); },

fetchMessages() { var result = API.getMessages(); this.dispatch(constants.FETCH_MESSAGES, result); }};

> dispatch an action to the dispatcher> multiple or 0 stores can listen

> may or may not have data> Good spot for business logic and error handling

> Can query stores or interface with API> See flux Pull Request

Page 15: Data Handling in React: Callbacks, Flux, and Redux

CALLING AN ACTION CREATOR FROM REACT COMPONENT

import React, { Component } from 'react';import { ListGroupItem, Button, Row, Col } from 'react-bootstrap';import { Icon } from 'components/Icon';

export class Call extends Component {

textBack = () => { this.props.flux.actions.addText(this.props); };

render() { return ( <ListGroupItem header={this.props.from} > <span>{this.props.number}</span> <Button bsStyle="success" onClick={this.textBack} className="text-caller" > <Icon type="envelope"/> </Button> </ListGroupItem> ); }}

Page 16: Data Handling in React: Callbacks, Flux, and Redux

STORES

var MessageStore = Fluxxor.createStore({ initialize() { this.messages = [];

this.bindActions( constants.FETCH_MESSAGES, this.onMessagesFetched, constants.DRAFT_TEXT, this.onDraftText ); },

getMessages() { return this.messages },

onMessagesFetched(messages) { this.messages = messages; this.emit('change'); },

onDraftText(textInfo) { this.messages.push({ id: this.messages.length + 1, from: textInfo.from, number: textInfo.number, status: "draft" });

this.emit('change'); }});

> Is NOT the same as a model, concerned with single applicaiton domain> Likely to have many

> emit is change events notifies views (components)> Any component anywhere can "listen" for data changes and read directly form it

> See flux Pull Request

Page 17: Data Handling in React: Callbacks, Flux, and Redux

USING THE STORE IN A REACT COMPONENT

export class TextMessageList extends Component {

constructor(props) { super(props); this.state = { messages: [] }; }

componentDidMount() { this.props.flux.store('MessageStore').on('change', this.updateMessages) }

componentWillUnmount() { this.props.flux.store('MessageStore').removeListener('change', this.updateMessages); }

updateMessages = () => { this.setState({ messages: this.props.flux.store('MessageStore').getMessages() }); };

messageNodes = () => { return this.state.messages.map( (message) => { return <TextMessage key={message.id} {...message} flux={this.props.flux} /> }); };

render() { ... }}

Page 18: Data Handling in React: Callbacks, Flux, and Redux

REDUX

> A very popular alternative to Flux, which focuses on developer experience

> Single Store> No Dispatcher

> Store consists of a reducer (pure function with no side effects)

Page 19: Data Handling in React: Callbacks, Flux, and Redux

THE REDUCER

function messages(state = [], action) { switch(action.type) { case 'DRAFT_TEXT': return state.concat([{ id: state.length + 1, from: action.from, number: action.number, status: "draft" }]); case 'FETCH_MESSAGES': return action.messages default: return state }}

> "updates" data> pure function to calculate next state from current state and an action

> must not mutate state> see redux pull request

Page 20: Data Handling in React: Callbacks, Flux, and Redux

COMPOSING REDUCERS

// sub reducerfunction calls(state = [], action) { if (action.type === 'FETCH_CALLS') { return action.calls else { return state }}

// main reducerfunction communcationsApp(state = {}, action) { return { messages: messages(state.messages, action), calls: calls(state.calls, action) }}

> A reducer can be composed of multiple sub reducers> redux has a helper combineReducers

> see redux pull request

Page 21: Data Handling in React: Callbacks, Flux, and Redux

STORE?

> The Store just takes a reducer to calculate state, and stores that value

> It is the only object required, similar to flux instance

Page 22: Data Handling in React: Callbacks, Flux, and Redux

REACT COMPONENT GETTING STATE FROM REDUX STORE

export class TextMessageList extends Component {

constructor(props) { super(props); this.state = { messages: [] }; }

componentDidMount() { this.unsubscribe = this.props.store.subscribe(this.updateMessages); }

componentWillUnmount() { this.unsubscribe(); }

updateMessages = () => { this.setState({ messages: this.props.store.getState().messages }); };

render() { ... }}

see redux pull request

Page 23: Data Handling in React: Callbacks, Flux, and Redux

ACTION CREATORS

export function addText(textInfo) { // example of error checking if ( textInfo.from ) { return Object.assign({}, textInfo, { type: 'DRAFT_TEXT'} ) } else { return {} }}

export function fetchCalls() { // not making async for simplicity return { type: 'FETCH_CALLS', calls: API.getCalls() }}

export function fetchMessages() { // not making async for simplicity return { type: 'FETCH_MESSAGES', messages: API.getMessages() }}

> Action Creators do not dispatch directly> They are shortcuts to return a properly formatted action object

> see redux pull request

Page 24: Data Handling in React: Callbacks, Flux, and Redux

USING THE ACTION CREATOR IN A COMPONENT

import React, { Component } from 'react';...import { addText } from 'myRedux/actions';

export class Call extends Component {

textBack = () => { this.props.store.dispatch(addText(this.props)); };

render() { return ( <ListGroupItem header={this.props.from} > <span>{this.props.number}</span> <Button bsStyle="success" onClick={this.textBack} className="text-caller" > <Icon type="envelope"/> </Button> </ListGroupItem> ); }}

see redux pull request

Page 25: Data Handling in React: Callbacks, Flux, and Redux

REACT COMPONENT TYPES

> Presentational Components> All behaviours are passed as props

> Only knows how to render UI> Container Components

> Connect with Stores> specify behavior

> Minimal or no markup

Page 26: Data Handling in React: Callbacks, Flux, and Redux

RESOURCES> React Ecosystem Overview (Pete Hunt)

https://github.com/petehunt/react-howto> React Transform Boilerplate

https://github.com/gaearon/react-transform-boilerplate

> Source Code from Talk https://github.com/droberts84/react-flux-

redux-talk

Page 27: Data Handling in React: Callbacks, Flux, and Redux

FLUX RESOURCES

> Official Flux page https://facebook.github.io/flux

> Alt http://alt.js.org/

> Fluxxor http://fluxxor.com/

Page 28: Data Handling in React: Callbacks, Flux, and Redux

REDUX RESOURCES

> Egghead.io course (Dan Abramov) https://egghead.io/series/getting-started-

with-redux> Offical Redux Page

http://redux.js.org/