99
Indeed My Jobs A case study in ReactJS and Redux

Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Embed Size (px)

Citation preview

Page 1: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Indeed My JobsA case study in ReactJS and Redux

Page 2: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Gaurav MathurSoftware Engineer

Page 3: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

I help people get jobs.

Page 4: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Page 5: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Page 6: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Page 7: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Page 8: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

What happens next?

Page 9: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Following up

Page 10: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Scheduling an interview

Page 11: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Preparing an interview

Page 12: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Negotiating your offer

Page 13: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Making a decision

Page 14: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Page 15: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Page 16: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Why React?

Page 17: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Virtual DOM

Page 18: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

JSX

Page 19: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Data flow

Page 20: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Page 21: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

MainDisplay

Page 22: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

ViewList

Page 23: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

JobTable

Page 24: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

JobRow

Page 25: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

MainDisplay

ViewListJobTable

JobRow

Page 26: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

MainDisplayState: jobs, counts

ViewListJobTable

JobRow

Page 27: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Props: counts

MainDisplayState: jobs, counts

ViewListJobTable

JobRow

Page 28: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

MainDisplayState: jobs, counts

ViewListJobTable

JobRow

Props: jobs, updateHandler

Page 29: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

MainDisplayState: jobs, counts

ViewListJobTable

JobRow

Props: job, updateHandler

Page 30: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Page 31: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

JobRow

MainDisplay

ViewListJobTable

this.props.handleUpdate(job, state)

Page 32: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

this.props.handleUpdate(job, state)

JobRow

MainDisplay

ViewListJobTable

Page 33: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

this.setState({jobs:newJobs, counts:newCounts});

JobRow

MainDisplay

ViewListJobTable

Page 34: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

this.render(); //automatically fired

JobRow

MainDisplay

ViewListJobTable

Page 35: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Component

Component

Component

Component

this.props.handleUpdate(job, state)

this.props.handleUpdate(job, state)

this.props.handleUpdate(job, state)

Page 36: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Page 37: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Flux

Page 38: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

View

Page 39: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

View

Action

Page 40: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

View

Dispatcher

Action

Page 41: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

View

Dispatcher

StoreAction

Page 42: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

View

Dispatcher

StoreAction

Page 43: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Page 44: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Transition the job

Transition Job

Job Row

Action = { type: JobActions.APPLY, job: job};

Page 45: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Dispatch the action

MyJobsDispatcher.handleAction({ type: JobActions.APPLY, job: job});

Transition Job

Dispatcher

Job Row

Page 46: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

function handleChange(action) { ... switch (action.type) { case JobActions.APPLY: job.set('state', States.APPLIED); break; ... } JobStore.emitChange();}JobStore.dispatchToken = MyJobsDispatcher.register(handleChange);

Update Job Store

Transition Job

Dispatcher

Job Store

Job Row

Page 47: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

function handleChange(action) { MyJobsDispatcher.waitFor( [JobStore.dispatchToken] ); ... JobCountsStore.emitChange();}

Update Job Counts

Transition Job

Dispatcher

Job StoreJob Count Store

Job Row

Page 48: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

ListenableStore.prototype = { emitChange() { for (let i = 0; i < this.listeners.length; i++) { this.listeners[i](); } }, ...}

Listen for changes

Page 49: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

const changeAggregator = new ChangeAggregator( MyJobsDispatcher, storesToListenTo);const MainDisplay = React.createClass({ componentDidMount() { changeAggregator.addChangeListener(this.update); } ...});

Collect changes

Page 50: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

const MainDisplay = React.createClass({ ... update() { this.setState({ jobs: JobStore.getJobs(), counts: JobCountStore.getCounts(), ... }); }});

Transition Job

Dispatcher

Job StoreJob Count Store

Job Row

View List Job Table

Update views

Page 51: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Shortcomings

Page 52: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Boilerplate

ListenableStore

ChangeAggregator

MyjobsDispatcher

Page 53: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Best Practices & Testability

// TODO: Should this be in a different place..?window.scrollTo(0, 0);ViewStore.emitChange();

ViewStore.js

Page 54: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Borrowed from: https://twitter.com/denisizmaylov/status/672390003188703238

Page 55: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Jing WangSoftware Engineer

Page 56: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

I help people get jobs.

Page 57: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Borrowed from: https://twitter.com/denisizmaylov/status/672390003188703238

Page 58: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Single source of truth

Page 59: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Application State Tree

Page 60: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

State is read only

Page 61: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Dispatcher & Actions

Page 62: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Pure functions

Page 63: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Reducers

Page 64: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Redux Store

Page 65: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Single source of truthconst createStore = (reducer) => { let state = {}; const getState = () => state; let listeners = []; const subscribe = (listener) => { listeners.push(listener); } const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); }; return {getState, dispatch, subscribe};}

Page 66: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

State is read onlyconst createStore = (reducer) => { let state = {}; const getState = () => state; let listeners = []; const subscribe = (listener) => { listeners.push(listener); } const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); }; return {getState, dispatch, subscribe};}

Page 67: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Changes are made with pure functionsconst createStore = (reducer) => { let state = {}; const getState = () => state; let listeners = []; const subscribe = (listener) => { listeners.push(listener); } const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); }; return {getState, dispatch, subscribe};}

Page 68: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Emit changesconst createStore = (reducer) => { let state = {}; const getState = () => state; let listeners = []; const subscribe = (listener) => { listeners.push(listener); } const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); }; return {getState, dispatch, subscribe};}

Page 69: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Single reducerconst createStore = (reducer) => { let state = {}; const getState = () => state; let listeners = []; const subscribe = (listener) => { listeners.push(listener); } const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); }; return {getState, dispatch, subscribe};}

Page 70: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

const myjobsReducer(state = new AppState(), action) => { state = state.set('jobs', jobsReducer(state.jobs, action)); state = state.set('jobCounts', jobCountsReducer(state.jobCounts, action, state.jobs)); ... return state;}

Combining reducers

Page 71: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

const myjobsStore = createStore(myjobsReducer);<MainDisplay store={myjobsStore} .../>

Store as an explicit prop

Page 72: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

const myjobsStore = createStore(myjobsReducer);<MainDisplay store={myjobsStore} .../><ViewList store={this.props.myjobsStore} .../>

Store as an explicit prop

Page 73: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

const myjobsStore = createStore(myjobsReducer);<MainDisplay store={myjobsStore} .../><ViewList store={this.props.myjobsStore} .../><JobTable store={this.props.myjobsStore} .../>

Store as an explicit prop

Page 74: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

react-redux Providerconst myjobsStore = createStore(myjobsReducer);<Provider store={myjobsStore}> <MainDisplay /></Provider>

MainDisplay

Provider

myjobsStore

Page 75: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

const myjobsStore = createStore(myjobsReducer);<Provider store={myjobsStore}> <MainDisplay /></Provider>

react-redux Provider

JobRow

MainDisplay

ViewListJobTable

Provider

myjobsStore

Provided store: {myjobsStore}

Page 76: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

connect(mapStateToProps, mapDispatchToProps, ...) {}

react-redux connect

Page 77: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

mapStateToProps

function mapStateToProps(state) { return { jobsState: state.jobs };}

Page 78: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

const mapDispatchToProps = (dispatch) => { return { handleAction: (action) => { dispatch(action); } };}

mapDispatchToProps

Page 79: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

connect(mapStateToProps, mapDispatchToProps, ...) { ... return function wrapWithConnect(WrappedComponent) { class Connect extends Component { ... return Connect; }; }}

react-redux connect

Page 80: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

react-redux connect

Connect

constructor(props, context) { this.store = props.store || context.store; this.state = { this.store.getState() };};

Page 81: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Connect

...componentDidMount() { this.store.subscribe(this.handleChange.bind(this));}handleChange() { this.setState({ this.store.getState() })}

react-redux connect

Page 82: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Connect

...render() { this.renderedElement = createElement( WrappedComponent, this.mergedProps ); return this.renderedElement;}

react-redux connect

Page 83: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Connect

...render() { this.renderedElement = createElement( WrappedComponent, this.mergedProps ); return this.renderedElement;}

mergedProps: { ...parentProps, ...mapStateToProps, ...mapDispatchToProps}

react-redux connect

Page 84: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

import { connect } from 'react-redux';...const matchStateToProps => (state) { return { jobsState: state.jobs };}module.exports = connect(matchStateToProps)(JobTable);

JobTable.js

Page 85: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

JobRow.js

import { connect } from 'react-redux';...module.exports = connect()(JobRow);

Page 86: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Connect

Connect Connect

Connect

JobRow

MainDisplay

ViewListJobTable

Page 87: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Page 88: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

const createStore = (reducer) => {... const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); };}

myjobsStore{type:"APPLY",jobKey:"7b14...",...}

Job Row

Page 89: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

const createStore = (reducer) => {... const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); };}

Job Row

myjobsStore

myjobsReducer

state

Page 90: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

const createStore = (reducer) => {... const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); };}

Job Row

myjobsStore

myjobsReducer

state

newState

Page 91: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

const createStore = (reducer) => {... const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); };}

JobTable

Job Row

myjobsStore

myjobsReducer

state

newState

Page 92: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

jobReducerJobStore

Flux stores to Redux reducers

...

Flux Redux

jobCountsReducerJobCountsStore

Page 93: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

... store.subscribe(() => { if (view !== newView) { window.scrollTo(0, 0); } });

Side effects in separate handlers

Page 94: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Less boilerplate code

createStoreListenableStoreChangeAggregatorMyJobsDispatcher

Flux ReduxFlux Redux

Page 95: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Less boilerplate code

connectaddChangeListener(

)JobStore.getJobs()

Flux Redux

Page 96: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Less boilerplate code

combineReducerswaitFor()

Flux Redux

Page 97: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Easier to unit test

Page 98: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

Well unit tested state transition logic

Less code to maintain

Code easier to follow

Results

Page 99: Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)

engineering.indeed.com

www.indeed.jobs