Modern Web Development With <React/> & Redux

tomas@wix.com

github.com/tqmukas

linkedin/miliauskas

Tomas Miliauskas

  • Software Architect & Front-End Guild Lead @Wix.com

  • Focusing on JavaScript technologies

  • Like Sci-Fi Movies

Agenda

Part 1: Welcome to <React/>

Part 2: Dive into <Components/>

Coffee break

Part 3: State management with Redux

Part 4: Testing <React/> apps

We're going to build

Part 1: Welcome to <React/>

CommonJS & Modules

// module print.js

module.exports = function print(msg) {
  console.log(msg);
};
// module info.js

module.exports.name = 'Bob';
module.exports.age = 26;
// module app.js

var print = require('./print.js');

modules.exports = function printInfo() {
  var info = require('./info.js');
  print(info.name + ' ' + info.age);
}

setTimeout(printInfo, 1500);

Tools & DEV tools

EcmaScript 6 features

// es6 modules
import React, {Component} from 'react';
export default App;
// destructuring
const {question, text} = data;
const [a, b] = [1, 2];
// arrow functions
const handleClick = (event) => {

};
// variables
let wrapper;
const guesses = [];
// classes
class App extends Component {
    constructor() {
        super();
    }
}    
// property shorthands
const foo = 'bar';
const data = {foo};
// array/object spread
const state = {...state, text: 'foo'};
const guesses = [...guesses, text];
// template strings
const text = `Lifes left: ${lives}`;

What is <React/>?

  • ​React is not a framework

  • It's a library to build user interfaces

  • Some people call it just a View layer

  • React is a philosophy

JSX: Part 1

<!-- HTML Element -->
<button
  class="primary"
  >Submit</button>
// React Element
React.createElement(
  'button',
  {className: 'primary'},
  'Submit'
);
<!-- Nested HTML -->
<div>
  <input type="text">
  <button
    class="primary"
    >Submit</button>
</div>
// Nested React
React.createElement('div',
  React.createElement('input', {type: 'text'}),
  React.createElement(
    'button',
    {className: 'primary'},
    'Submit'
  )
);

JSX: Part 2

<!-- JSX Element -->
<button
  className="primary"
  >Submit</button>
<!-- Nested JSX -->
<div>
  <input type="text">
  <button
    className="primary"
    >Submit</button>
</div>
// Nested React
React.createElement('div',
  React.createElement('input', {type: 'text'}),
  React.createElement(
    'button',
    {className: 'primary'},
    'Submit'
  )
);
// React Element
React.createElement(
  'button',
  {className: 'primary'},
  'Submit'
);

Virtual DOM

  1. State changes

  2. A completely new Virtual DOM is build

  3. Previous Virtual DOM tree is compared to the new one

  4. Only minimal changes are made in the actual DOM

App

DOM

Virtual DOM

Build/Modify

Build/Modify

Send Events

Send Events

It is time for coding

  • Make sure you have an internet connection

  • Make sure you have all the tools installed

  • Checkout/download the project from github
    https://github.com/tqmukas/workshop-modern-react-and-redux

  • Run yarn or npm install

  • Run yarn start or npm start

Part 2: Dive into <Components/>

<Component/> Concept

Template - JSX

Logic - JS,

Appearance - CSS, Assets - PNG, JSON

Create Component in <React/>

// Using ES5

var Alphabet = React.createClass({
  render: function () {
      return React.createElement(
        'div', null, 'Alphabet'
      );
  }
});



ReactDOM.render(
  <Alphabet/>,
  document.getElementById('root')
);
// Using ES6

import React, {Component} from 'react';
import ReactDOM from 'react-dom';

class Alphabet extends Component {
  render() {
      return (<div>Alphabet</div>);
  }
});


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

JSX Attributes & Interpolation

// simple values
import React, {Component} from 'react';
import ReactDOM from 'react-dom';

class DevDaysLink extends Component {
  render() {
    return (
      <a
        href="http://devdays.lt"
        >DevDays 2017</a>
    );
  }
}


ReactDOM.render(
  <DevDaysLink/>,
  document.getElementById('root')
);
// variables
import React, {Component} from 'react';
import ReactDOM from 'react-dom';

class DevDaysLink extends Component {
  render() {
    const year = 2017;
    const link = 'http://devdays.lt';

    return (
      <a href={link}>DevDays {year}<a>
    );
  }
}

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

Input & Output

import React, {Component} from 'react';
import PropTypes from 'prop-types';

class DevDaysButton extends Component {
  render() {
    const {link} = this.props; // Input through props
    const {onButtonClick} = this.props // Output through events
    return (<Button href={link}
        onClick={(event) => onButtonClick(event)} // Synthetic event
        >DevDays 2017</Button>);
  }
}
DevDaysButton.propTypes = {
  link: PropTypes.string.isRequired, // Required attribute
  onButtonClick: PropTypes.func // Optional attribute
};

// Usage
ReactDOM.render(<DevDaysButton link="http://devdays.lt"
  onButtonClick={event => console.log(event)}/>);

Functional & Class Components

// Functional

import React from 'react';

function Hangman(props) {
  return (
    <img
      src={props.src}
      alt="hangman"
      />
  );
}

export default Hangman;
// Class

import React, {Component} from 'react';

class Hangman extends Component {
  render() {
    return (
      <img
        src={this.props.src}
        alt="hangman"
        />
    ); 
  }
}

export default Hangman;

Class Components: Lifecycle

// Mounting
constructor()
componentWillMount()
render()
componentDidMount()
// Unmounting
componentWillUnmount()
// Updating
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate()

Class Components: State

// How to use state properly
import React, {Component} from 'react';

class App extends component {
  constructor(props) {
    super(props);
    this.state = {animal: 'Dog'}; // create state only in constructor
  }

  componentDidMount() {
    this.setState({animal: 'Kitty'}); // modify state only via method
  }

  render() {
    const {animal} = this.state; // state object to access properties
    return (<div>Hello {animal}</div>);
  }
}

Applying Style

// Regular styling
import React from 'react';
import './Button.css';

function PrimaryButton({label}) {
  return (
    <button
      className="primary"
      >{label}</button>
  );
}
// Styling within CSS modules
import React from 'react';
import styles from './Button.css';

function PrimaryButton({label}) {
  return (
    <button
      className={styles.primary}
      >{label}</button>
  );
}
/* Button.css */
.primary {
  background-color: blue;
  color: white;
}

Let's get back to coding...

...and finish our Game

Coffee break

Part 3: Managing state with Redux

Redux (http://redux.js.org)

  • Redux is state container for your Apps

  • It's extremely simple, although it looks complicated when parts moving 

  • It has nothing to do with <React/>

  • Provides great developer experience

Core Concepts of Redux

State

UI

Store

Reducer

Actions

Defines

Triggers

Sends

Updates

Contains

<Components/> by Categories

Presentational

  • How things look

  • Usually Functional components

  • Just input and output through props

  • Knows nothing about Redux and have no state

Container

  • How things work

  • Usually Class components

  • Wire up Presentational components

  • Connects Redux or manage internal state

...connect our Game to Redux

Hands-on session again...

Part 4: Testing <React/> apps

Default Testkit for <React/>

React Test Utilities - low level testkit

https://facebook.github.io/react/docs/test-utils.html

import React from 'react';
import TestUtils from 'react-dom/test-utils';


describe('Testing with React Test Utils', () => {
  it('should render div as a DOM component', () => {
    const div = TestUtils.renderIntoDocument(<div/>);
    expect(TestUtils.isDOMComponent(div)).toBe(true);
  );
});

Low level testkit is not enough

Enzyme by Airbnb

Core API is based on 3 methods:

// Shallow rendering

import { shallow } from 'enzyme';
const wrapper = shallow(<Component/>);
// Static rendering

import { render } from 'enzyme';
const wrapper = render(<Component/>);

Using react-test-utils under the hood, http://airbnb.io/enzyme/

// Full rendering

import { mount } from 'enzyme';
const wrapper = mount(<Component/>);

Jest: more than a test runner

test('two plus two is four', () => {
  expect(2 + 2).toBe(4);
});

// Or

it('two plus two is four', () => {
  expect(2 + 2).toBe(4);
});
  • Was rewritten

  • Easy setup

  • Very fast

  • Accurate feedback

  • Built-in spies and mocks

  • Can do snapshot testing

Testing Environment

JSX enables testing inside NodeJS environment using JSDOM, which means no browser is needed.

$ jest --env=jsdom

Using Jest

Component Testing Philosophy

Given component is an atomic unit and apps are basically component trees...

...doesn't matter if it's a unit test or

an integration test.

We always test only components, just in different levels

Now let's write some tests...

Thanks for Attending

tomas@wix.com

github.com/tqmukas

linkedin/miliauskas

Modern Web Development With React & Redux

By Tomas Miliauskas

Modern Web Development With React & Redux

  • 1,386