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
-
react-scripts (from create-react-app)
-
webpack for bundling
-
babel for transpilling JS and JSX to ES5
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
-
State changes
-
A completely new Virtual DOM is build
-
Previous Virtual DOM tree is compared to the new one
-
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