React Unplugged

React Unplugged

A Q&A about React and friends. Presented at JS.Talks.

Links from presentation:

- slides
- https://facebook.github.io/react
- http://isfiberreadyyet.com
- https://www.youtube.com/watch?v=ZCuYPiUIONs
- http://redux.js.org
- https://github.com/erikras/ducks-modular-redux
- http://graphql.org/
- http://www.apollodata.com/
- styling
- https://github.com/css-modules/css-modules
- https://github.com/styled-components/styled-components
- ui
- 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
- forms
- 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
- testing
- 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
- react
- https://github.com/JedWatson/react-select
- https://github.com/ayrton/react-key-handler
- https://github.com/twitter-fabric/velocity-react
- https://github.com/ianstormtaylor/slate
- https://github.com/andreypopp/react-textarea-autosize
- https://github.com/brigade/react-waypoint
- https://github.com/acdlite/recompose
- https://github.com/cloudflare/react-gateway
- https://github.com/One-com/react-truncate
- https://github.com/react-component/progress
- https://github.com/react-component
- redux
- 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
- utilities
- https://github.com/lodash/lodash
- https://github.com/moment/moment
- https://github.com/JedWatson/classnames
- https://github.com/kolodny/immutability-helper

7a0e72a6f55811246bb5d9a946fd2e49?s=128

Radoslav Stankov

August 13, 2017
Tweet

Transcript

  1. React Unplugged Radoslav Stankov 13/06/2017

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

  3. None
  4. None
  5. None
  6. https://speakerdeck.com/rstankov/react-unplugged

  7. None
  8. None
  9. None
  10. ! React " Fiber # Redux $ GraphQL
 % Cool

    libraries
  11. ! React " Fiber # Redux $ GraphQL
 % Cool

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

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

    libraries
  14. None
  15. React 
 https://facebook.github.io/react


  16. Virtual DOM

  17. Virtual Dom React Element React Element React Element React Element

    React Element
  18. Virtual Dom DOM Element DOM Element DOM Element DOM Element

    DOM Element
  19. Virtual Dom React Element React Element React Element React Element

    React Element
  20. 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
  21. Fiber

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


  24. Context

  25. 
 Component

  26. 
 Component Child Component

  27. 
 Component Child Component Child Child Component

  28. 
 Component Child Component Child Child Component 
 Child Child

    … Child Component
  29. 
 Component Child Component Child Child Component 
 Child Child

    … Child Component Context
  30. 
 Component Child Component Child Child Component 
 Child Child

    … Child Component Context Context
  31. Forms

  32. export default function SubmissionForm() { return ( <form action="/submission"> <h2>Speaker</h2>

    <div> <label htmlFor="speakerName">Name: </label> <input type="text" id="speakerName" name="speakerName" defaultValue="" /> </div> <div> <label htmlFor="speakerEmail">Email: </label> <input type="email" id="speakerEmail" name="speakerEmail" defaultValue="" /> </div> <input type="submit" value="Submit" /> </form> ); }
  33. 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 ( <form onSubmit={this.handleSubmit}> <input type="text" value={this.state.value} onChange={this.handleChange} /> <input type="submit" value="Submit" /> </form> ); } }
  34. 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 ( <form onSubmit={this.handleSubmit}> <input type="text" value={this.state.value} onChange={this.handleChange} /> <input type="submit" value="Submit" /> </form> ); } } Input change
  35. 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 ( <form onSubmit={this.handleSubmit}> <input type="text" value={this.state.value} onChange={this.handleChange} /> <input type="submit" value="Submit" /> </form> ); } } Input change 
 handleChange setState
  36. 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 ( <form onSubmit={this.handleSubmit}> <input type="text" value={this.state.value} onChange={this.handleChange} /> <input type="submit" value="Submit" /> </form> ); } } Input change 
 handleChange setState 
 render New value
  37. components/ modules/ pages/ pages/Layout/ utils/
 config.js index.js paths.js routes.js

  38. Redux 
 http://redux.js.org


  39. Store

  40. React View Store

  41. Action React View Store

  42. Reducer Action React View Store

  43. Reducer Action React View New Store

  44. Reducer Action React View New Store

  45. None
  46. None
  47. Ducks: Redux Reducer Bundles 
 https://github.com/erikras/ducks-modular-redux


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

  49. function action() { return async (dispatch, getState) => { //

    do ... async stuff dispatch({ type: ACTION_1 }); // do ... async stuff dispatch({ type: ACTION_2 }); // ...so on }; } redux-thunk
  50. import { createStore, applyMiddleware } from 'redux'; import thunk from

    'redux-thunk'; import reducer from './reducers'; const store = createStore(rootReducer, applyMiddleware(thunk));
  51. 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 }); }; }
  52. this.props.filterChange(filterName, this.props.api);

  53. 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 }); }; }
  54. 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));
  55. 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 }); }; }
  56. 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 }); }; }
  57. class Filters extends React.Component { filterHander(filterName) { return () =>

    { this.props.dispatch(changeFilter(filterName)); }; } render() { return ( <div> {Object.keys(FILTERS).map((filter) => ( <button key={filter} onClick={this.filterHander(filter)}> {filter} </button> ))} </div> ); } }
  58. 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([]); }); });
  59. /modules/anchor.js /modules/context.js /modules/cookies.js /modules/currentUser.js /modules/notice.js /modules/seed.js /modules/visitedTopics.js

  60. modules/[reducer].js 1. Action Constants 2. Action Creators 3. Reducer 4.

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

    Selectors 5. Other stuff ¯\_(ϑ)_/¯
  62. 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<?Notice> = function createReducer(null, { [RECEIVE_NOTICE]: (state, { payload: { notice } }) => notice, });
 export default reducer;
  63. 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); }); }); });
  64. 
 http://graphql.org/


  65. POST /graphql

  66. 
 query { topic(id: 1) { id name description isFollowed

    image } } POST /graphql
  67. 
 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 } } }
  68. 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 } } }
  69. 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 } } }
  70. 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 } } }
  71. 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 } } }
  72. 
 mutation FollowTopic($input) { followTopic(input: $input) { node { id

    isFollowed } } } POST /graphql { "data": {
 "followTopic": { "node": { "id": 1, "isFollowed": true } } } }
  73. 
 http://www.apollodata.com/


  74. None
  75. None
  76. None
  77. Header Media Recommended Posts Makers Discussion

  78. /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
  79. // 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 } } }
  80. // pages/Post/RecommendedPosts/Fragment.graphql #import "ph/pages/Post/RecommendedPosts/Item/Fragment.graphql" fragment PostRecommendedPosts on Post { recommended_posts(first:

    10) { edges { node { ...PostRecommendedPostsItem } } } }
  81. // 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 } }
  82. import QUERY from './Query.graphql'; import { graphql } from 'react-apollo';

    // ... export default graphql(QUERY, { options: ({ params: { id } }) => ({ variables: { id, }, }), });
  83. None
  84. ( https://github.com/css-modules/css-modules
 
 ) https://github.com/styled-components/styled-components Styling

  85. ( 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
  86. ( 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
  87. ( 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
  88. ) 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
  89. ( 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
  90. ( https://github.com/lodash/lodash ) https://github.com/moment/moment * https://github.com/JedWatson/classnames + https://github.com/kolodny/immutability-helper Utilities

  91. None