React + Redux

in this workshop!?

From zero to Redux

/by @kjellski

Agenda

  • JavaScript
  • React
  • Redux

JavaScript

let & const
// var does not have block scope
var name = 'Ryan';
if (true) {
  var name = 'Michael';
  name // 'Michael'
}
name // 'Michael'
// let has block scope
let name = 'Ryan';
if (true) {
  let name = 'Michael';
  name // 'Michael'
}
name // 'Ryan'

// const has block scope too
const name = 'Ryan';
if (true) {
  const name = 'Michael';
  name // 'Michael'
}
name // 'Ryan'
// let can be reassigned
let isOpen = true;
isOpen = false;
isOpen // false

// const cannot be reassigned
const isOpen = true;
isOpen = false; // throws error
string templates
const something = 'ugly stuff';
const str = 'instead of ' + something + ' like this';

const something = 'lovely stuff';
const str = `you can do ${something} like this`;

const str = `
  also
  multiline
  is totally cool
`;
const obj = {
  insteadOfThis: function() {
    // do stuff
  },

  youCanDoThis() {
    // do stuff
  }
};
object functions
arrow functions =>
const obj = {
  url: '/api/stuff',

  fetch(users) {
    users.forEach((user) => {
      // `this` is the `this` from outside this function because
      // there is no context inside an arrow function
      return getUser(`${this.url}/${user.id}`);
    })
  }
};
const add = function(x, y) { return x + y };

// becomes
const add = (x, y) => { return x + y };

// which can be shorter with explicit expression return
const add = (x, y) => x + y;

// if we want multiline, we can create an expression with ()
const add = (x, y) => (
  x + y
);
implicit return
arrays
const numbers = [ 1, 2, 3, 4, 5 ];

// map converts an array to a new, transformed array
const doubled = numbers.map((number) => {
  return number * 2;
})
doubled // [ 2, 4, 6, 8, 10 ]

// filter, return false to remove from an array
const lessThan3 = numbers.filter((n) => {
  return n < 3;
})
lessThan3 // [ 1, 2 ]

// remember, that can be super short
const lessThan3 = numbers.filter(n => n < 3);
destructuring
const obj = { x: 1, y: 2, z: { a: 3 } };

// instead of:
const x = obj.x;
const y = obj.y;

// we can "destructure" the values off
const { x, y } = obj;
x // 1
y // 2

// you can use this all over the place, like function parameters
const add = ({x, y}) => x + y;
add({x: 3, y: 4}) // 7

// you can use nested destructuring as well
const { z: { a } } = obj;
a // 3
modules
// instead of cjs
var React = require('react');

// we use ES modules
import React from 'react';
import ReactDOM from 'react-dom';

// and with destructuring
import { render } from 'react-dom';

So, how does it work?

Introducing JSX

const element = <h1>Hello, world!</h1>;
function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

const element = (
  <h1>
    Hello, {formatName(user)}!
  </h1>
);
function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}
const element = <div tabIndex="0"></div>;

const element = <img src={user.avatarUrl} />;
!(HTML || string)

can embed expressions

is an expression itself

can have attributes/props

Components

// pure component / functional component
const HelloWorldComponent = () => (
  <h1>Hello World!</h1>
);

ReactDOM.render(
  <HelloWorldComponent />,
  document.getElementById('root')
);

Pure Components

  • no internal state
  • no lifecycle methods
  • "just a function"
  • start here and try to stay pure

Try here: goo.gl/aut1X7

Components

// stateful class component
class HelloWorldComponent extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      counter: 1,
      name: 'Xing'
    };
      
    window.setInterval(this.incrementCounter, 5000);
    
    console.log('constructor');
  }

  incrementCounter = () => {
    console.log('counter++');
    let { counter } = this.state;
    counter++;
    this.setState({
      counter
    });
  }
  
  componentWillMount = () => {
    console.log('componentWillMount');
  }

  componentDidMount = () => {
    console.log('componentDidMount');
  }

  render () {
    const { name, counter } = this.state;
    const { greeting } = this.props;
    
    console.log('render');
    
    return (
      <h1>
        {greeting} {name}{'!'.repeat(counter)}
      </h1>
    );
  }
}

console.log('start');
ReactDOM.render(
  <HelloWorldComponent greeting="Hello" />,
  document.getElementById('root')
);

Stateful Components

  • internal state
  • lifecycle methods
  • "just a class"
  • end here, it's complex

Components

Real World Example:

Brewery -> TextInput

And from there on,

it's just LEGO™...

... and now you:

Go here and start with 'Hello World':

https://github.com/ReactTraining/react-workshop

Shared State

  • Overview
  • Store
  • Actions
  • Reducer
  • Usage with react

premature optimization,

the root of all evil...

Overview

Actions

const ADD_TODO = 'ADD_TODO'
const addTodo = (text) => {
  return {
    type: ADD_TODO,
    text
  };
};

const REMOVE_TODO = 'REMOVE_TODO'
const removeTodo = (id) => {
  return {
    type: REMOVE_TODO,
    id
  };
};

...
  • Triggered by views
  • Plain Javascript Objects
  • Conventions up to you
const ADD_TODO = 'ADD_TODO'
const addTodo = (text) => {
  return {
    type: ADD_TODO,
    payload: { text }
  };
};

const REMOVE_TODO = 'REMOVE_TODO'
const removeTodo = (id) => {
  return {
    type: REMOVE_TODO,
    payload: { id }
  };
};

...

Async Actions

export const REQUEST_POSTS = 'REQUEST_POSTS'
const requestPosts = (subreddit) => ({ type: REQUEST_POSTS, subreddit });

export const RECEIVE_POSTS = 'RECEIVE_POSTS'
const receivePosts = (subreddit, json) => ({
    type: RECEIVE_POSTS,
    subreddit,
    posts: json.data.children.map(child => child.data),
    receivedAt: Date.now()
});

export const fetchPosts = (subreddit) => {
  return (dispatch) => {
    dispatch(requestPosts(subreddit));

    return fetch(`https://www.reddit.com/r/${subreddit}.json`)
      .then(
        response => response.json(),
        error => console.log('An error occurred.', error)
      )
      .then(json =>
        dispatch(receivePosts(subreddit, json))
      );
  }
}

Reducer

  • design the state
  • just functions
  • take state and action
  • apply actions to state
  • does nothing else!
// (previousState, action) => newState

import {
  ADD_TODO,
  ...
} from './actions';

// initial state could be defined as
const initialState = { todos: [] }

export const todoApp = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TODO:
      return Object.assign({}, state, {
        todos: [
          ...state.todos,
          {
            text: action.payload.text,
            completed: false
          }
        ]
      })
    ...
    default:
      return state
  }
}

Store

  • One store to rule them all
  • structure defined by reducers
  • plain JSON
  • only data
  • serializable
import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)

Demo

Questions

&

Answers?

Thanks!

React + Redux

By Kjell Otto

React + Redux

This is a talk about the what and how of react + redux for Single-page Frontend Applications

  • 1,753