Slide 1

Slide 1 text

React Unplugged Radoslav Stankov 13/06/2017

Slide 2

Slide 2 text

Radoslav Stankov @rstankov http://rstankov.com http://github.com/rstankov

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

https://speakerdeck.com/rstankov/react-unplugged

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

! React " Fiber # Redux $ GraphQL
 % Cool libraries

Slide 11

Slide 11 text

! React " Fiber # Redux $ GraphQL
 % Cool libraries &

Slide 12

Slide 12 text

! React " Fiber # Redux $ GraphQL
 % Cool libraries '

Slide 13

Slide 13 text

! React " Fiber # Redux $ GraphQL
 % Cool libraries

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

React 
 https://facebook.github.io/react


Slide 16

Slide 16 text

Virtual DOM

Slide 17

Slide 17 text

Virtual Dom React Element React Element React Element React Element React Element

Slide 18

Slide 18 text

Virtual Dom DOM Element DOM Element DOM Element DOM Element DOM Element

Slide 19

Slide 19 text

Virtual Dom React Element React Element React Element React Element React Element

Slide 20

Slide 20 text

Virtual Dom React Element React Element React Element React Element React Element React Element React Element React Element React Element React Element DOM Element DOM Element DOM Element DOM Element DOM Element DOM Element DOM Element DOM Element DOM Element DOM Element

Slide 21

Slide 21 text

Fiber

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text


 https://www.youtube.com/watch?v=ZCuYPiUIONs


Slide 24

Slide 24 text

Context

Slide 25

Slide 25 text


 Component

Slide 26

Slide 26 text


 Component Child Component

Slide 27

Slide 27 text


 Component Child Component Child Child Component

Slide 28

Slide 28 text


 Component Child Component Child Child Component 
 Child Child … Child Component

Slide 29

Slide 29 text


 Component Child Component Child Child Component 
 Child Child … Child Component Context

Slide 30

Slide 30 text


 Component Child Component Child Child Component 
 Child Child … Child Component Context Context

Slide 31

Slide 31 text

Forms

Slide 32

Slide 32 text

export default function SubmissionForm() { return (

Speaker

Name:
Email:
); }

Slide 33

Slide 33 text

class ExampleForm extends React.Component { state = { value: '' } handleChange = (event) => { this.setState({value: event.target.value}); } handleSubmit = (event) => { e.preventDefault(); remoteCall(this.target.value)/ }; render() { return ( ); } }

Slide 34

Slide 34 text

class ExampleForm extends React.Component { state = { value: '' } handleChange = (event) => { this.setState({value: event.target.value}); } handleSubmit = (event) => { e.preventDefault(); remoteCall(this.target.value)/ }; render() { return ( ); } } Input change

Slide 35

Slide 35 text

class ExampleForm extends React.Component { state = { value: '' } handleChange = (event) => { this.setState({value: event.target.value}); } handleSubmit = (event) => { e.preventDefault(); remoteCall(this.target.value)/ }; render() { return ( ); } } Input change 
 handleChange setState

Slide 36

Slide 36 text

class ExampleForm extends React.Component { state = { value: '' } handleChange = (event) => { this.setState({value: event.target.value}); } handleSubmit = (event) => { e.preventDefault(); remoteCall(this.target.value)/ }; render() { return ( ); } } Input change 
 handleChange setState 
 render New value

Slide 37

Slide 37 text

components/ modules/ pages/ pages/Layout/ utils/
 config.js index.js paths.js routes.js

Slide 38

Slide 38 text

Redux 
 http://redux.js.org


Slide 39

Slide 39 text

Store

Slide 40

Slide 40 text

React View Store

Slide 41

Slide 41 text

Action React View Store

Slide 42

Slide 42 text

Reducer Action React View Store

Slide 43

Slide 43 text

Reducer Action React View New Store

Slide 44

Slide 44 text

Reducer Action React View New Store

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

Ducks: Redux Reducer Bundles 
 https://github.com/erikras/ducks-modular-redux


Slide 48

Slide 48 text

function action() { return { type: ACTION }; } redux-thunk

Slide 49

Slide 49 text

function action() { return async (dispatch, getState) => { // do ... async stuff dispatch({ type: ACTION_1 }); // do ... async stuff dispatch({ type: ACTION_2 }); // ...so on }; } redux-thunk

Slide 50

Slide 50 text

import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import reducer from './reducers'; const store = createStore(rootReducer, applyMiddleware(thunk));

Slide 51

Slide 51 text

function changeFilter(filterName, api) { return async function(dispatch, getState) { if (getState().filter == filter) { return; } dispatch({ type: 'TODO/LOAD', filter: filter }); const todos = await api.loadTodos({filter: filter}); dispatch({ type: 'TODO/LOADED', todos: todos }); }; }

Slide 52

Slide 52 text

this.props.filterChange(filterName, this.props.api);

Slide 53

Slide 53 text

function changeFilter(filterName, api) { return async function(dispatch, getState) { if (getState().filter == filter) { return; } dispatch({ type: 'TODO/LOAD', filter: filter }); const todos = await api.loadTodos({filter: filter}); dispatch({ type: 'TODO/LOADED', todos: todos }); }; }

Slide 54

Slide 54 text

import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import reducer from './reducers'; import apiClient from './utils/apiClient'; const api = apiClient(); const store = createStore(rootReducer, applyMiddleware(thunk.withExtraArgument(api));

Slide 55

Slide 55 text

function changeFilter(filterName, api) { return async function(dispatch, getState) { if (getState().filter == filter) { return; } dispatch({ type: 'TODO/LOAD', filter: filter }); const todos = await api.loadTodos({filter: filter}); dispatch({ type: 'TODO/LOADED', todos: todos }); }; }

Slide 56

Slide 56 text

function changeFilter(filterName) { return async function(dispatch, getState, api) { if (getState().filter == filter) { return; } dispatch({ type: 'TODO/LOAD', filter: filter }); const todos = await api.loadTodos({filter: filter}); dispatch({ type: 'TODO/LOADED', todos: todos }); }; }

Slide 57

Slide 57 text

class Filters extends React.Component { filterHander(filterName) { return () => { this.props.dispatch(changeFilter(filterName)); }; } render() { return (
{Object.keys(FILTERS).map((filter) => ( {filter} ))}
); } }

Slide 58

Slide 58 text

import createStore from 'tests/support/createStore'; import factory from 'tests/support/factory'; describe(changeFilter.name, () => { it('loads todos', () => { const store = createStore({ filter: 'all' }); const todo = factory.todo(); store.api.stub('loadTodos', [todo]); store.dispatch(changeFilter('completed')); expect(store.getState().filter).to.equal('completed'); expect(store.getState().todos).to.deep.equal([todo]); }); it('does not reload todos when filter is not changing', () => { const store = createStore({ filter: 'all' }); const todo = factory.todo(); store.api.stub('loadTodos', [todo]); store.dispatch(changeFilter('all')); expect(store.getState().todos).to.deep.equal([]); }); });

Slide 59

Slide 59 text

/modules/anchor.js /modules/context.js /modules/cookies.js /modules/currentUser.js /modules/notice.js /modules/seed.js /modules/visitedTopics.js

Slide 60

Slide 60 text

modules/[reducer].js 1. Action Constants 2. Action Creators 3. Reducer 4. Selectors 5. Other stuff ¯\_(ϑ)_/¯

Slide 61

Slide 61 text

modules/[reducer].js 1. Action Constants 2. Action Creators 3. Reducer 4. Selectors 5. Other stuff ¯\_(ϑ)_/¯

Slide 62

Slide 62 text

import { createReducer } from 'ph/redux'; import type { Dispatchable, Reducer } from 'ph/types/redux'; import type { Notice } from 'ph/types/Notice'; // Constants const RECEIVE_NOTICE = 'NOTICE/RECEIVE'; // Actions export function receiveNotice(notice: Notice): Dispatchable { return { type: RECEIVE_NOTICE, payload: { notice } }; } export function clearNotice(): Dispatchable { return { type: RECEIVE_NOTICE, payload: { notice: null } }; } // Reducer
 const reducer: Reducer = function createReducer(null, { [RECEIVE_NOTICE]: (state, { payload: { notice } }) => notice, });
 export default reducer;

Slide 63

Slide 63 text

import createTestStore from 'ph/createTestStore'; import { clearNotice, receiveNotice } from 'ph/modules/notice'; describe('notice', () => { const getState = (store) => store.getState().notice; describe(receiveNotice.name, () => { it('sets a notice object', () => { const store = createTestStore(); const notice = { type: 'notice', message: 'submitted' } store.dispatch(receiveNotice(notice)); expect(getState(store)).to.deep.equal(notice); }); }); describe(clearNotice.name, () => { it('sets a notice object', () => { const store = createTestStore(); store.dispatch(clearNotice()); expect(getState(store)).to.equal(null); }); }); });

Slide 64

Slide 64 text


 http://graphql.org/


Slide 65

Slide 65 text

POST /graphql

Slide 66

Slide 66 text


 query { topic(id: 1) { id name description isFollowed image } } POST /graphql

Slide 67

Slide 67 text


 query { topic(id: 1) { id name description isFollowed image } } POST /graphql { "data": { "topic": { "id": 1, "name": "Tech", "description": "Hardware or "isFollowed": true, "image": "assets.producthun } } }

Slide 68

Slide 68 text

query { topic(id: 1) { id ...Item } } fragment Item on Topic { id name description ...Button ...Image } fragment Button on Topic { id name isFollowed } fragment Image on Topic { image } POST /graphql { "data": { "topic": { "id": 1, "name": "Tech", "description": "Hardware or "isFollowed": true, "image": "assets.producthun } } }

Slide 69

Slide 69 text

query { topic(id: 1) { id ...Item } } fragment Item on Topic { id name description ...Button ...Image } fragment Button on Topic { id name isFollowed } fragment Image on Topic { image } POST /graphql { "data": { "topic": { "id": 1, "name": "Tech", "description": "Hardware or "isFollowed": true, "image": "assets.producthun } } }

Slide 70

Slide 70 text

query { topic(id: 1) { id ...Item } } fragment Item on Topic { id name description ...Button ...Image } fragment Button on Topic { id name isFollowed } fragment Image on Topic { image } POST /graphql { "data": { "topic": { "id": 1, "name": "Tech", "description": "Hardware or "isFollowed": true, "image": "assets.producthun } } }

Slide 71

Slide 71 text

query { topic(id: 1) { id ...Item } } fragment Item on Topic { id name description ...Button ...Image } fragment Button on Topic { id name isFollowed } fragment Image on Topic { image } POST /graphql { "data": { "topic": { "id": 1, "name": "Tech", "description": "Hardware or "isFollowed": true, "image": "assets.producthun } } }

Slide 72

Slide 72 text


 mutation FollowTopic($input) { followTopic(input: $input) { node { id isFollowed } } } POST /graphql { "data": {
 "followTopic": { "node": { "id": 1, "isFollowed": true } } } }

Slide 73

Slide 73 text


 http://www.apollodata.com/


Slide 74

Slide 74 text

No content

Slide 75

Slide 75 text

No content

Slide 76

Slide 76 text

No content

Slide 77

Slide 77 text

Header Media Recommended Posts Makers Discussion

Slide 78

Slide 78 text

/pages/Post/Discussion/Fragment.graphql /pages/Post/Discussion/index.js /pages/Post/Discussion/styles.css /pages/Post/RecommendedPosts/Fragment.graphql /pages/Post/RecommendedPosts/index.js /pages/Post/RecommendedPosts/styles.css /pages/Post/RecommendedPosts/Item/... /pages/Post/Header/... /pages/Post/Makers/... /pages/Post/Media/... /pages/Post/Query.graphql /pages/Post/index.js /pages/Post/styles.js

Slide 79

Slide 79 text

// pages/Post/Discussion/Fragment.graphql
 fragment PostDiscussion on Post { comments { id created_at parent_comment_id state votes_count body user { id username name headline } } }

Slide 80

Slide 80 text

// pages/Post/RecommendedPosts/Fragment.graphql #import "ph/pages/Post/RecommendedPosts/Item/Fragment.graphql" fragment PostRecommendedPosts on Post { recommended_posts(first: 10) { edges { node { ...PostRecommendedPostsItem } } } }

Slide 81

Slide 81 text

// pages/Post/PostPage.graphql #import "ph/lib/meta/MetaTags.graphql" #import "./Discussion/Fragment.graphql" #import "./Header/Fragment.graphql" #import "./Makers/Fragment.graphql" #import "./Media/Fragment.graphql" #import "./RecommendedPosts/Fragment.graphql" query PostPage($id: String!) { post(id: $id) { id
 ...MetaTags ...PostDiscussion ...PostHeader ...PostMedia ...PostRecommendedPosts } }

Slide 82

Slide 82 text

import QUERY from './Query.graphql'; import { graphql } from 'react-apollo'; // ... export default graphql(QUERY, { options: ({ params: { id } }) => ({ variables: { id, }, }), });

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

( https://github.com/css-modules/css-modules
 
 ) https://github.com/styled-components/styled-components Styling

Slide 85

Slide 85 text

( https://github.com/Semantic-Org/Semantic-UI-React ) https://github.com/callemall/material-ui * https://github.com/react-bootstrap/react-bootstrap + https://github.com/gabrielbull/react-desktop , https://github.com/palantir/blueprint - https://github.com/grommet/grommet UI

Slide 86

Slide 86 text

( https://github.com/seeden/react-form-controlled ) https://github.com/erikras/redux-form * https://github.com/25th-floor/revalidation + https://github.com/codecks-io/react-reform , https://github.com/tannerlinsley/react-form - https://github.com/prometheusresearch/react-forms Form

Slide 87

Slide 87 text

( https://github.com/facebook/jest ) https://github.com/jasmine/jasmine * https://github.com/airbnb/enzyme/ + https://github.com/chaijs/chai , https://github.com/producthunt/chai-enzyme - https://github.com/mochajs/mocha . https://github.com/sinonjs/sinon Testing

Slide 88

Slide 88 text

) https://github.com/ayrton/react-key-handler * https://github.com/twitter-fabric/velocity-react + https://github.com/ianstormtaylor/slate - https://github.com/brigade/react-waypoint . https://github.com/acdlite/recompose / https://github.com/cloudflare/react-gateway 0 https://github.com/One-com/react-truncate 1 https://github.com/react-component React

Slide 89

Slide 89 text

( https://github.com/elgerlambert/redux-localstorage ) https://github.com/gaearon/redux-thunk * https://github.com/redux-saga/redux-saga + https://github.com/markdalgleish/redux-analytics , https://www.npmjs.com/package/reselect - https://www.npmjs.com/package/normalizr Redux

Slide 90

Slide 90 text

( https://github.com/lodash/lodash ) https://github.com/moment/moment * https://github.com/JedWatson/classnames + https://github.com/kolodny/immutability-helper Utilities

Slide 91

Slide 91 text

No content