Loading deck

React & Redux

Disclaimer

This is a different way of developing an app

Disclaimer

Different strokes for different folks

A few things before we start...

The const keyword

const constantValue = 'banana'

//This would cause an error
constantValue = 'orange'

Arrow Functions

const subtract = (x, y) => {
    return x - y
}

console.log(subtract(3, 2))
//prints 1

Arrow Functions

const divide = (x, y) => x / y

console.log(divide(3, 2))
//prints 1.5

Arrow Functions

const stringReturner =
    () => 'this function returns a string'

console.log(stringReturner())
//prints this function returns a string

Arrow Functions

const increment = x => x + 1

console.log(increment(1))
//prints 2

Arrow Functions

//If you're returning an object with a one-liner
//you need to wrap the object in parentheses

const returnObject = () => ({a: 1, b: 2})

console.log(returnObject())
//prints { a: 1, b: 2 }

Time for some

react code

Extremely simple example

<html>
    <head>
        <title>simple-react</title>
    </head>
    <body>
        <div id='root'></div>
    </body>
    <script src='js/main.js'></script>
</html>

Extremely simple example

const
    React = require('react'),
    ReactDOM = require('react-dom'),
    render = () =>
        ReactDOM.render(
            <div>
                <h1>Hey this is a header!</h1>
                <p>Hey this is a paragraph!!</p>
            </div>,
            document.getElementById('root'))

render()

Extremely simple example

and another thing...

Destructuring

const addAB = ({a, b}) => a + b

console.log(addAB({a: 4, b: 5}))
//prints 9

const {thing} = {
    thing: 'this is a thing',
    stuff: 'this is some stuff'
}
console.log(thing)
//Prints this is a thing

Moar React

components

const
    Header = ({titleText}) =>
        <h1>{titleText}</h1>

components

const
    Paragraph = ({text}) =>
        <p>{text}</p>

components

render = () =>
     ReactDOM.render(
         <div>
             <Header titleText='Yo Yo, What Up?'/>
             <Paragraph
                 text='This is a paragraph. Deal with the real!'/>
         </div>,
         document.getElementById('root'))

Components

components

const
    React = require('react'),
    ReactDOM = require('react-dom'),
    Header = ({titleText}) =>
        <h1>{titleText}</h1>,
    Paragraph = ({text}) =>
        <p>{text}</p>,
    render = () =>
        ReactDOM.render(
            <div>
                <Header titleText='Yo Yo, What Up?'/>
                <Paragraph
                    text='This is a paragraph. Deal with the real!'/>
            </div>,
            document.getElementById('root'))

render()

CSS IN JS

cSS in JS

body {
    background-color: #EAC435;
    font-family: 'PT Sans', sans-serif;
}

cSS in JS

foreGroundStyle = {
    backgroundColor: '#345995',
    color: '#03CEA4',
    padding: '.3rem',
    borderRadius: '.5rem',
    marginRight: '1rem',
    marginLeft: '1rem',
}

cSS in JS

Header = ({titleText}) =>
    <h1 style={foreGroundStyle}>{titleText}</h1>

cSS in JS

Paragraph = ({text}) =>
    <p style={foreGroundStyle}>{text}</p>

CSS IN JS

a Few More Things...

map

console.log(
    [1, 2, 3]
        .map(x => 2 * x))

//prints [2, 4, 6]

map

console.log(
    [1, 2, 3]
        .map(x => 2 * x)
        .map(x => x + 3)
        .map(x => x * x))

//prints [ 25, 49, 81 ]

map

console.log(
    ['Fluttershy', 'Twilight Sparkle', 'Pinkie Pie']
        .map((pony, i) => (i + 1) + ') ' + pony)
        .join('\n'))
/*
prints
1) Fluttershy
2) Twilight Sparkle
3) Pinkie Pie
*/

Object Shorthand

const
    superHero = (name, secretIdentity, powers) => ({
        name: name, secretIdentity: secretIdentity, powers: powers
    })
console.log(
    superHero(
        'Spider-Man',
        'Peter Parker',
        [
            'Wall crawling',
            'Super Strength',
            'Spider Sense'
        ]))
/*
Prints
{ name: 'Spider-Man',
  secretIdentity: 'Peter Parker',
  powers: [ 'Wall crawling', 'Super Strength', 'Spider Sense' ] }
*/

Object Shorthand

const
    superHero = (name, secretIdentity, powers) => ({
        name, secretIdentity, powers
    })
console.log(
    superHero(
        'Spider-Man',
        'Peter Parker',
        [
            'Wall crawling',
            'Super Strength',
            'Spider Sense'
        ]))
/*
Prints
{ name: 'Spider-Man',
  secretIdentity: 'Peter Parker',
  powers: [ 'Wall crawling', 'Super Strength', 'Spider Sense' ] }
*/

mapping components

Mapping Components

paragraphs = [
    'Life is like a hurricane',
    'Here in Duckburg',
    'Race cars, lasers, aeroplanes',
    'It\'s a duck blur!',
]

Mapping Components

{/*error if we don't add the key*/}
<Header titleText='Yo Yo, What Up?'/>
{paragraphs
    .map((paragraph, key) =>
        ({text: paragraph, key}))
    .map(({text, key}) =>
        <Paragraph text={text} key={key}/>)}

Mapping components

Here is a thing

default Arguments

const
    defaultAdd = (x = 1, y = 2) => x + y

console.log(defaultAdd(3, 4))
//prints 7

console.log(defaultAdd(3))
//prints 5

console.log(defaultAdd())
//prints 3

Let's Build a to-do app

To-Do App

TO-Do App

Header = ({titleText}) =>
    <div style={foregroundStyle}>
        {titleText} <Button text='+'/>
    </div>

TO-Do App

Button = ({text}) =>
    <button
        style={buttonStyle}
        type="button">
            {text}
    </button>

TO-Do App

Item = ({text}) =>
    <div style={foregroundStyle}>
        <Button text='▲'/>
        <Button text='▼'/>
        <input
            type="text"
            readOnly='readonly'
            style={{width: '15rem'}}
            value={text}/>
        <Button text='-'/>
    </div>

TO-Do App

toDos = [
    'climb Mt. Everest',
    'become an astronaut',
    'win the Nobel Peace Prize'
],
{createStore} = require('redux'),
reducer = (state = toDos) => state,
store = createStore(reducer),

TO-Do App

<div>
    <Header titleText='To Do List'/>
    {store.getState()
        .map((text, key) => ({text, key}))
        .map(({text, key}) =>
            <Item text={text} key={key}/>)}
</div>

To-Do App

moar things for you

Object Spreading

const
    object1 = {
        a: 1,
        b: 2
    },
    object2 = {
        ...object1,
        c: 3,
        d: 4
    }

console.log(object2)
//prints { a: 1, b: 2, c: 3, d: 4 }

Filter

console.log(
    [1, 2, 3, 4, 5]
        .filter(x => x % 2 !== 0))
//prints [1, 3, 5]

Pure Functions

const
    add = (x, y) => x + y

console.log(add(1, 2))
//prints 3

ImPure Functions

var
    someVar = 1,
    dependsOnSomeVar = x => x / someVar

console.log(dependsOnSomeVar(5))
//prints 5

someVar = 5

console.log(dependsOnSomeVar(5))
//now prints 1

Adding/

Removing

To Do Items

TO-Do App

toDos = [
    'climb Mt. Everest',
    'become an astronaut',
    'win the Nobel Peace Prize'
],
{createStore} = require('redux'),
reducer = (state = toDos) => state,
store = createStore(reducer),

Adding/Removing

To Do Items

initialState = {
    nextKey: 1,
    toDos: []
}
reducer = (state = initialState, action) => {
    switch(action.type) {
//more code below

Adding/Removing

To Do Items

default:
    return state

Adding/Removing

To Do Items

case 'REMOVE':
    return {
        ...state,
        toDos: state.toDos
            .filter(toDo =>
                toDo.id !== action.id)
    }

Adding/Removing

To Do Items

case 'ADD':
    return {
        ...state,
        nextId: state.nextId + 1,
        toDos: [{
            key: state.nextId,
            id: state.nextId,
            text: 'Do a thing'
        }]
            .concat(state.toDos)
    }

Adding/Removing

To Do Items

//This is a pure function
reducer = (state = initialState, action) => {
    switch(action.type) {
        case 'ADD':
            return {
                ...state,
                nextKey: state.nextKey + 1,
                toDos: state.toDos.concat({
                    key: state.nextKey,
                    id: state.nextKey,
                    text: 'Do a thing'
                })
            }
        case 'REMOVE':
            return {
                ...state,
                toDos: state.toDos
                    .filter(toDo =>
                        toDo.id !== action.id)
            }
        default:
            return state
    }
}

The Reducer is a pure function

Adding/Removing

To Do Items

Header = ({titleText}, {store}) =>
    <div
        style={foregroundStyle}
        onClick={() => store.dispatch({type: 'ADD'})}>
            {titleText} <Button text='+'/>
    </div>

Header.contextTypes = {
    store: PropTypes.object
}

Adding/Removing

To Do Items

Item = ({text, id}, {store}) =>
    <div style={foregroundStyle}>
        <Button text='▲'/>
        <Button text='▼'/>
        <input
            type="text"
            readOnly='readonly'
            style={{width: '15rem'}}
            value={text}/>
        <Button
            text='-'
            onClick={() => store.dispatch({type: 'REMOVE', id})}/>
    </div>

Item.contextTypes = {
    store: PropTypes.object
}

To-Do App

Moar things!1!

Destructuring Renaming

const divideRename = ({x: a, y: b}) => a / b

console.log(divideRename({x: 1, y: 2}))
//Prints 0.5

Reduce, ReduceRight

console.log(
    [1, 2, 3, 4]
        .reduce((sum, x) => sum + x, 0))
//Prints 10

console.log(
    'reverse'
        .split('')
        .reduceRight((str, c) => str + c, ''))
//Prints esrever

Changing Text

Changing Text

case 'CHANGE_TEXT':
    return {
        ...state,
        toDos: state.toDos.reduce(
            (toDos, toDo) =>
                toDo.id === action.id
                    ? toDos.concat({
                        ...toDo,
                        text: action.text
                    })
                    : toDos.concat(toDo),
            [])
    }

CHANGING TEXT

onChange={({target:{value: text}}) =>
    store.dispatch({type: 'CHANGE_TEXT', id, text})}

To-Do App

Moving Items

Moving Items

case 'MOVE_UP':
    return {
        ...state,
        toDos: state.toDos.reduce(
            (toDos, toDo) =>
                toDo.id === action.id
                    ? addOneFromBottom(toDo, toDos)
                    : toDos.concat(toDo),
            [])
    }

Moving Items

case 'MOVE_DOWN':
    return {
        ...state,
        toDos: state.toDos.reduceRight(
            (toDos, toDo) =>
                toDo.id === action.id
                    ? addOneFromTop(toDo, toDos)
                    : [toDo].concat(toDos),
            [])
    }

Moving Items

Item = ({text, id}, {store}) =>
    <div style={foregroundStyle}>
        <Button
            onClick={() =>
                store.dispatch({type: 'MOVE_UP', id})}
            text='▲'/>
        <Button
            onClick={() =>
                store.dispatch({type: 'MOVE_DOWN', id})}
            text='▼'/>

To-Do App

To-Do App

Moving Items

last = arr => arr[arr.length - 1],
head = arr => arr[0],
tail = arr => arr.slice(1),
allButLast = arr => arr.slice(0, -1),
lastOrEmpty = arr => last(arr) ? last(arr) : [],
addOneFromBottom = (el, arr) =>
    allButLast(arr)
        .concat(el)
        .concat(lastOrEmpty(arr)),
addOneFromTop = (el, arr) =>
    [head(arr)]
        .concat(el)
        .concat(tail(arr))

Live Example

Resources