I'm Luke

@luke_dot_js

lukewestby

luke.westby@raise.com

+ Redux

// TODO:

  • Explain React
  • Explain Redux
  • Set up a project
  • Build an app

React

Virtual DOM 

+

One-way Data Flow

<Components />
import React, { Component, PropTypes } from 'react';

export default class Greeter extends Component {
    
    static propTypes = {
        name: PropTypes.string.isRequired
    };

    state = {
        currentSeconds: 0,
        timeout: null
    };

    componentDidMount() {

        const timeout = setTimeout(() => {
            this.setState({
                currentSeconds: this.state.currentSeconds + 1
            });
        }, 1000);

        this.setState({ timeout });
    }

    componentWillUnmount() {
        clearTimeout(this.state.timeout);
    }

    render() {
        return (
            <div>
                <p>Hello, {this.props.name}!</p>
                <p>{this.state.currentSeconds} have elapsed</p>
            </div>
        );
    }
}
import React from 'react';
import Greeter from './Greeter';

React.render(
    <Greeter name="JS.Chi" />,
    document.querySelector('main')
);

React Key Concepts

  • Props
    • Immutable, passed in
  • State
    • Mutable, internal
  • Rendering
    • Pure functions
    • Return a React Component (Virtual DOM)
    • Frequently implemented with JSX
  • Lifecycle Hooks
    • Do manual setup/teardown
    • Control updating

Redux

  • One state container
  • State is only mutable by actions
  • Mutations are pure functions
// actions.js

export const INCREMENT_COUNT = 'INCREMENT_COUNT';

export function incrementCount(value) {
    return { type: INCREMENT_COUNT, value };
}

export const DECREMENT_COUNT = 'DECREMENT_COUNT';

export function decrementCount(value) {
    return { type: DECREMENT_COUNT, value };
}
// reducers.js

import { combineReducers } from 'redux';

import {
    INCREMENT_COUNT,
    DECREMENT_COUNT
} from './actions.js';

function count(count = 0, action) {
    switch(action.type) {
    
    case INCREMENT_COUNT:
        return count + action.value;

    case DECREMENT_COUNT:
        return count - action.value;
    
    default:
        return count;
    }
}

export default combineReducers({
    count
});
// app.js

import React, { Component } from 'react';
import { createStore } from 'redux';
import { connect, Provider } from 'react-redux';

import {
    incrementCount,
    decrementCount
} from './actions';

import appReducer from './reducers';

@connect((store) => store)
class App extends Component {
    
    render() {
        const { count, dispatch } = this.props;
        return (
            <div>
                <span>{count}</span>
                <button onClick={() => dispatch(incrementCount(1))}>+1</button>
                <button onClick={() => dispatch(decrementCount(1))}>-1</button>
            </div>
        );
    }
}

const appStore = createStore(appReducer);
React.render(
    <Provider store={appStore}>
        {() => <App />}
    </Provider>,
    document.querySelector('main')
);

Other concerns

  • Build
    • Babel
    • Webpack, Browserify + Gulp, etc.
  • Server rendering
    • React.renderToString()
  • Styling
    • CSS Modules
    • Stilr, Radium, etc.
  • Testing
    • React.addons.TestUtils
    • jsdom

    • Any test runner

Demo

App
Header
CommentsList
CommentBox
Comment
Comment

JS.Chi React Demo

By lukewestby

JS.Chi React Demo

Demonstration of building a React application start-to-finish for the Chicago JavaScript Meetup on August 25, 2015

  • 1,531