React + Redux
in this workshop!?
From zero to Redux
/by @kjellski
/by @hpcodecraft
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
Try here: https://goo.gl/4Uov99
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