├── .babelrc
├── .eslintrc
├── .gitignore
├── LICENSE
├── README.md
├── index.html
├── package.json
├── server.js
├── src
├── App.js
├── DeckController.js
├── components
│ ├── Center.js
│ ├── Center.less
│ ├── Deck.js
│ ├── Deck.less
│ ├── Image.js
│ ├── Image.less
│ ├── Slide.js
│ └── Slide.less
├── favicon.ico
├── index.js
├── index.less
├── normalize.less
├── slides
│ ├── actionCreators.js
│ ├── actionTypes.js
│ ├── bundle1.png
│ ├── bundle2.png
│ ├── community.png
│ ├── community0.png
│ ├── escher.jpg
│ ├── hor.png
│ ├── hor2.png
│ ├── hor3.png
│ ├── hor4.png
│ ├── hor5.png
│ ├── index.js
│ ├── logo.png
│ ├── middleware.png
│ ├── middleware2.png
│ ├── middleware3.png
│ ├── middleware4.png
│ ├── middleware5.png
│ ├── middleware6.png
│ ├── middleware7.png
│ ├── middleware8.png
│ ├── middleware9.png
│ ├── next.png
│ ├── npm.png
│ ├── reducer.png
│ ├── reducers.js
│ ├── selector.png
│ ├── setstate2.png
│ ├── store.png
│ ├── store2.png
│ ├── store3.png
│ ├── store4.png
│ ├── store5.png
│ ├── store6.png
│ ├── store7.png
│ ├── store8.png
│ ├── tom.jpg
│ └── twitter.png
└── theme.less
├── webpack.config.js
└── webpack.prod.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "stage": 0
3 | }
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "ecmaFeatures": {
3 | "jsx": true,
4 | "modules": true
5 | },
6 | "env": {
7 | "browser": true,
8 | "es6": true,
9 | "node": true
10 | },
11 | "parser": "babel-eslint",
12 | "rules": {
13 | "eol-last": 2,
14 | "max-len": [2, 80, 4],
15 | "no-underscore-dangle": [0],
16 | "no-var": 2,
17 | "new-cap": 0,
18 | "react/display-name": 0,
19 | "react/jsx-boolean-value": 2,
20 | "react/jsx-quotes": 2,
21 | "react/jsx-no-undef": 2,
22 | "react/jsx-sort-props": 0,
23 | "react/jsx-uses-react": 2,
24 | "react/jsx-uses-vars": 2,
25 | "react/no-did-mount-set-state": 2,
26 | "react/no-did-update-set-state": 2,
27 | "react/no-multi-comp": 0,
28 | "react/no-unknown-property": 2,
29 | "react/react-in-jsx-scope": 2,
30 | "react/self-closing-comp": 2,
31 | "react/wrap-multilines": 2,
32 | "quotes": [2, "single"],
33 | "space-before-function-paren": [2, {
34 | "anonymous": "always",
35 | "named": "never"
36 | }],
37 | "strict": [2, "global"]
38 | },
39 | "plugins": [
40 | "react"
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | build
4 |
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Dan Abramov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The Redux Journey
2 |
3 | ## [Watch the Talk](https://youtu.be/uvAXVMwHJXU)
4 | ## [View the Slides](http://gaearon.github.io/the-redux-journey)
5 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | The Redux Journey
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
20 |
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "the-redux-jorney",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "start": "node server.js",
6 | "build": "webpack --config webpack.prod.config.js"
7 | },
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/gaearon/the-redux-journey.git"
11 | },
12 | "author": "Dan Abramov (http://github.com/gaearon)",
13 | "license": "MIT",
14 | "devDependencies": {
15 | "babel-core": "^5.1.11",
16 | "babel-eslint": "^3.1.7",
17 | "babel-loader": "^5.0.0",
18 | "css-loader": "^0.13.1",
19 | "eslint": "^0.21.2",
20 | "eslint-plugin-react": "^2.3.0",
21 | "file-loader": "^0.8.4",
22 | "image-webpack-loader": "^1.8.0",
23 | "less": "^2.5.1",
24 | "less-loader": "^2.2.0",
25 | "react-hot-loader": "^1.2.2",
26 | "style-loader": "^0.12.2",
27 | "webpack": "^1.7.2",
28 | "webpack-dev-server": "^1.7.0"
29 | },
30 | "dependencies": {
31 | "classnames": "^1.2.2",
32 | "lodash": "^3.9.3",
33 | "object-assign": "^2.0.0",
34 | "react": "^15.1.0",
35 | "react-dom": "^15.1.0",
36 | "react-modal": "^0.2.0",
37 | "react-redux": "^4.4.5",
38 | "redux": "^3.5.2"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var WebpackDevServer = require('webpack-dev-server');
3 | var config = require('./webpack.config');
4 |
5 | new WebpackDevServer(webpack(config), {
6 | publicPath: config.output.publicPath,
7 | hot: true,
8 | historyApiFallback: true,
9 | watchDelay: 0,
10 | stats: { colors: true }
11 | }).listen(3000, 'localhost', function (err, result) {
12 | if (err) {
13 | console.log(err);
14 | }
15 |
16 | console.log('Listening at localhost:3000');
17 | });
18 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import DeckController from './DeckController';
3 | import { connect } from 'react-redux';
4 | import * as actionCreators from './slides/actionCreators';
5 | import './normalize.less';
6 | import './index.less';
7 |
8 | export default connect(
9 | state => state,
10 | actionCreators
11 | )(DeckController);
12 |
--------------------------------------------------------------------------------
/src/DeckController.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Deck from './components/Deck';
3 | import Slide from './components/Slide';
4 | import getSlides from './slides';
5 |
6 | export default class DeckController extends Component {
7 | constructor() {
8 | super();
9 | this.handleKeyDown = this.handleKeyDown.bind(this);
10 | }
11 |
12 | componentDidMount() {
13 | window.addEventListener('keydown', this.handleKeyDown);
14 | }
15 |
16 | handleKeyDown(e) {
17 | switch (e.keyCode) {
18 | case 37:
19 | this.props.prevSlide();
20 | break;
21 | case 39:
22 | this.props.nextSlide();
23 | break;
24 | }
25 | }
26 |
27 | render() {
28 | return (
29 |
30 | {this.renderSlides()}
31 |
32 | );
33 | }
34 |
35 | renderSlides() {
36 | return getSlides().map((content, index) =>
37 | {content}
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/Center.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import './Center.less';
3 |
4 | export default class Center extends Component {
5 | static propTypes = {
6 | wide: PropTypes.bool,
7 | gradient: PropTypes.bool
8 | };
9 |
10 | render() {
11 | const { wide, alt, code, gradient, title } = this.props;
12 | return (
13 |
22 |
23 | {this.props.children}
24 |
25 |
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/Center.less:
--------------------------------------------------------------------------------
1 | @import ".././theme.less";
2 |
3 | .Center {
4 | width: 100%;
5 | height: 100%;
6 | display: flex;
7 | align-items: center;
8 | justify-content: center;
9 |
10 | background-color: @base07;
11 | color: @base01;
12 |
13 | h1,h2,h3,h4,h5,h6 {
14 | color: @base00;
15 | }
16 | }
17 |
18 | .Center.Center--alt {
19 | background-color: @base00;
20 | color: @base06;
21 |
22 | h1,h2,h3,h4,h5,h6 {
23 | color: @base07;
24 | }
25 | }
26 |
27 | .Center.Center--gradient {
28 | background: linear-gradient(to top, @base00, @base07);
29 | }
30 |
31 |
32 | .Center.Center--code {
33 | background-color: #1B2B34;
34 | }
35 |
36 | .Center-content {
37 | max-width: 55%;
38 | text-align: center;
39 | }
40 |
41 | .Center--wide .Center-content {
42 | max-width: 80%;
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/Deck.js:
--------------------------------------------------------------------------------
1 | import React, { cloneElement, Component, Children, PropTypes } from 'react';
2 | import './Deck.less';
3 |
4 | const iOS = /iphone|ipod|ipad/.test(window.navigator.userAgent.toLowerCase());
5 |
6 | export default class Deck extends Component {
7 | static propTypes = {
8 | currentSlide: PropTypes.number.isRequired
9 | };
10 |
11 | render() {
12 | const { children, currentSlide } = this.props;
13 | const elements = [];
14 |
15 | Children.forEach(children, (child, index) => {
16 | if (index === currentSlide) {
17 | elements.unshift(
18 | cloneElement(child, { key: 'current' })
19 | );
20 | } else if (!iOS) {
21 | elements.push(
22 | this.preloadImages(child, index)
23 | );
24 | }
25 | });
26 |
27 |
28 | return (
29 |
30 |
41 | {elements}
42 |
53 |
54 | );
55 | }
56 |
57 | preloadImages(slide, index) {
58 | return (
59 |
68 | {slide}
69 |
70 | );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/Deck.less:
--------------------------------------------------------------------------------
1 | .Deck {
2 | height: 100vh;
3 | width: 100vw;
4 | }
5 |
--------------------------------------------------------------------------------
/src/components/Image.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './Image.less';
3 |
4 | export default class Image extends Component {
5 | render() {
6 | return (
7 |
8 | );
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/Image.less:
--------------------------------------------------------------------------------
1 | .Image {
2 | max-width: 100%;
3 | }
--------------------------------------------------------------------------------
/src/components/Slide.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './Slide.less';
3 |
4 | export default class Slide extends Component {
5 | render() {
6 | return (
7 |
8 |
9 | {this.props.children}
10 |
11 |
12 | );
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/Slide.less:
--------------------------------------------------------------------------------
1 | @import ".././theme.less";
2 |
3 | .Slide {
4 | width: 100vw;
5 | height: 100vh;
6 | }
7 |
8 | .Slide-content {
9 | position: relative;
10 | width: 100%;
11 | height: 100%;
12 | overflow: hidden;
13 | }
14 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import './favicon.ico';
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import App from './App';
5 | import { createStore } from 'redux';
6 | import { Provider } from 'react-redux';
7 | import reducer from './slides/reducers';
8 |
9 | const store = createStore(
10 | reducer,
11 | (window.devToolsExtension ?
12 | window.devToolsExtension() :
13 | (f => f)
14 | )
15 | );
16 |
17 | ReactDOM.render(
18 |
19 |
20 | ,
21 | document.getElementById('root')
22 | );
23 |
24 | if (module.hot) {
25 | module.hot.accept('./slides/reducers', () => {
26 | const nextReducer = require('./slides/reducers');
27 | store.replaceReducer(nextReducer);
28 | });
29 | }
--------------------------------------------------------------------------------
/src/index.less:
--------------------------------------------------------------------------------
1 | @import "theme.less";
2 |
3 | body {
4 | text-rendering: optimizeLegibility;
5 | font-family: @font-main;
6 | font-size: @fontsize-root;
7 | }
8 |
9 | @media only screen
10 | and (min-device-width : 375px)
11 | and (max-device-width : 667px) {
12 | body {
13 | font-size: 1.75em;
14 | }
15 | }
16 |
17 | code {
18 | font-family: @font-code;
19 | }
20 |
21 | blockquote {
22 | font-weight: bold;
23 | }
24 |
25 | ul {
26 | text-align: left;
27 | }
28 |
29 | * {
30 | box-sizing: border-box;
31 | }
32 |
--------------------------------------------------------------------------------
/src/normalize.less:
--------------------------------------------------------------------------------
1 | /*! normalize.css v3.0.1 | MIT License | git.io/normalize */
2 |
3 | // * 1. Set default font family to sans-serif.
4 | // * 2. Prevent iOS text size adjust after orientation change, without disabling user zoom.
5 | html {
6 | font-family: sans-serif;
7 | -ms-text-size-adjust: 100%;
8 | -webkit-text-size-adjust: 100%;
9 | }
10 |
11 | // Remove default margin.
12 | body { margin: 0 }
13 |
14 | // HTML5 display definitions ==========================================================================
15 | // * Correct `block` display not defined for any HTML5 element in IE 8/9.
16 | // * Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox.
17 | // * Correct `block` display not defined for `main` in IE 11.
18 | article,
19 | aside,
20 | details,
21 | figcaption,
22 | figure,
23 | footer,
24 | header,
25 | hgroup,
26 | main,
27 | nav,
28 | section,
29 | summary {
30 | display: block;
31 | }
32 |
33 | // * 1. Correct `inline-block` display not defined in IE 8/9.
34 | // * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
35 | audio,
36 | canvas,
37 | progress,
38 | video {
39 | display: inline-block;
40 | vertical-align: baseline;
41 | }
42 |
43 | // * Prevent modern browsers from displaying `audio` without controls.
44 | // * Remove excess height in iOS 5 devices.
45 | audio:not([controls]) {
46 | display: none;
47 | height: 0;
48 | }
49 |
50 | // * Address `[hidden]` styling not present in IE 8/9/10.
51 | // * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
52 | [hidden],
53 | template {
54 | display: none;
55 | }
56 |
57 | // Links ==========================================================================
58 | // * Remove the gray background color from active links in IE 10.
59 | a {
60 | background: transparent;
61 | // * Improve readability when focused and also mouse hovered in all browsers.
62 | &:active,
63 | &:hover {
64 | outline: 0;
65 | }
66 | }
67 |
68 | // Text-level semantics ==========================================================================
69 |
70 | // * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
71 | abbr[title] { border-bottom: 1px dotted; }
72 |
73 | // * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
74 | b,
75 | strong {
76 | font-weight: bold;
77 | }
78 |
79 | // * Address styling not present in Safari and Chrome.
80 | dfn { font-style: italic; }
81 |
82 | // * Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari, and Chrome.
83 | h1 {
84 | font-size: 2em;
85 | margin: 0.67em 0;
86 | }
87 |
88 | // * Address styling not present in IE 8/9.
89 | mark {
90 | background: #ff0;
91 | color: #000;
92 | }
93 |
94 | // * Address inconsistent and variable font size in all browsers.
95 | small { font-size: 80%; }
96 |
97 | // * Prevent `sub` and `sup` affecting `line-height` in all browsers.
98 | sub,
99 | sup {
100 | font-size: 75%;
101 | line-height: 0;
102 | position: relative;
103 | vertical-align: baseline;
104 | }
105 |
106 | sup { top: -0.5em; }
107 | sub { bottom: -0.25em; }
108 |
109 | // Embedded content ==========================================================================
110 | // * Remove border when inside `a` element in IE 8/9/10.
111 | img { border: 0; }
112 |
113 | // * Correct overflow not hidden in IE 9/10/11.
114 | svg:not(:root) { overflow: hidden; }
115 |
116 | // Grouping content ==========================================================================
117 | // * Address margin not present in IE 8/9 and Safari.
118 | figure { margin: 1em 40px; }
119 |
120 | // * Address differences between Firefox and other browsers.
121 | hr {
122 | -moz-box-sizing: content-box;
123 | box-sizing: content-box;
124 | height: 0;
125 | }
126 |
127 | // * Contain overflow in all browsers.
128 | pre { overflow: auto; }
129 |
130 | // * Address odd `em`-unit font size rendering in all browsers.
131 | code,
132 | kbd,
133 | pre,
134 | samp {
135 | font-family: monospace, monospace;
136 | font-size: 1em;
137 | }
138 |
139 | // Forms ==========================================================================
140 | // * Known limitation: by default, Chrome and Safari on OS X allow very limited
141 | // * styling of `select`, unless a `border` property is set.
142 |
143 | // * 1. Correct color not being inherited.
144 | // * Known issue: affects color of disabled elements.
145 | // * 2. Correct font properties not being inherited.
146 | // * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
147 | button,
148 | input,
149 | optgroup,
150 | select,
151 | textarea {
152 | color: inherit;
153 | font: inherit;
154 | margin: 0;
155 | }
156 |
157 | // * Address `overflow` set to `hidden` in IE 8/9/10/11.
158 | button { overflow: visible;}
159 |
160 | // * Address inconsistent `text-transform` inheritance for `button` and `select`.
161 | // * All other form control elements do not inherit `text-transform` values.
162 | // * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
163 | // * Correct `select` style inheritance in Firefox.
164 | button,
165 | select {
166 | text-transform: none;
167 | }
168 |
169 | // * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls.
170 | // * 2. Correct inability to style clickable `input` types in iOS.
171 | // * 3. Improve usability and consistency of cursor style between image-type `input` and others.
172 | button,
173 | html input[type="button"] {
174 | -webkit-appearance: button;
175 | cursor: pointer;
176 | }
177 |
178 | // * Re-set default cursor for disabled elements.
179 | button[disabled],
180 | html input[disabled] {
181 | cursor: default;
182 | }
183 |
184 | // * Remove inner padding and border in Firefox 4+.
185 | button
186 | input {
187 | &::-moz-focus-inner {
188 | border: 0;
189 | padding: 0;
190 | }
191 | }
192 |
193 | // * Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet.
194 | input {
195 | line-height: normal;
196 | &[type="reset"],
197 | &[type="submit"] {
198 | -webkit-appearance: button;
199 | cursor: pointer;
200 | }
201 |
202 | // * It's recommended that you don't attempt to style these elements.
203 | // * Firefox's implementation doesn't respect box-sizing, padding, or width.
204 | // * 1. Address box sizing set to `content-box` in IE 8/9/10.
205 | // * 2. Remove excess padding in IE 8/9/10.
206 | &[type="checkbox"],
207 | &[type="radio"] {
208 | box-sizing: border-box;
209 | padding: 0;
210 | }
211 |
212 | // * Fix the cursor style for Chrome's increment/decrement buttons. For certain
213 | // * `font-size` values of the `input`, it causes the cursor style of the
214 | // * decrement button to change from `default` to `text`.
215 | &[type="number"] {
216 | &::-webkit-inner-spin-button,
217 | &::-webkit-outer-spin-button {
218 | height: auto;
219 | }
220 | }
221 |
222 | // * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
223 | // * 2. Address `box-sizing` set to `border-box` in Safari and Chrome (include `-moz` to future-proof).
224 | &[type="search"] {
225 | -webkit-appearance: textfield;
226 | -moz-box-sizing: content-box;
227 | -webkit-box-sizing: content-box;
228 | box-sizing: content-box;
229 |
230 | // * Remove inner padding and search cancel button in Safari and Chrome on OS X.
231 | // * Safari (but not Chrome) clips the cancel button when the search input has
232 | // * padding (and `textfield` appearance).
233 | &::-webkit-search-cancel-button,
234 | &::-webkit-search-decoration {
235 | -webkit-appearance: none;
236 | }
237 | }
238 |
239 | }
240 |
241 | // * Define consistent border, margin, and padding.
242 | fieldset {
243 | border: 1px solid #c0c0c0;
244 | margin: 0 2px;
245 | padding: 0.35em 0.625em 0.75em;
246 | }
247 |
248 | // * 1. Correct `color` not being inherited in IE 8/9/10/11.
249 | // * 2. Remove padding so people aren't caught out if they zero out fieldsets.
250 | legend {
251 | border: 0;
252 | padding: 0;
253 | }
254 |
255 | // * Remove default vertical scrollbar in IE 8/9/10/11.
256 | textarea { overflow: auto; }
257 |
258 | // * Don't inherit the `font-weight` (applied by a rule above).
259 | // * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
260 | optgroup { font-weight: bold; }
261 |
262 | // Tables ==========================================================================
263 | // * Remove most spacing between table cells.
264 | table {
265 | border-collapse: collapse;
266 | border-spacing: 0;
267 | }
268 |
269 | td,
270 | th {
271 | padding: 0;
272 | }
--------------------------------------------------------------------------------
/src/slides/actionCreators.js:
--------------------------------------------------------------------------------
1 | import * as types from './actionTypes';
2 |
3 | export function prevSlide() {
4 | return { type: types.PREV_SLIDE };
5 | }
6 |
7 | export function nextSlide() {
8 | return { type: types.NEXT_SLIDE };
9 | }
10 |
--------------------------------------------------------------------------------
/src/slides/actionTypes.js:
--------------------------------------------------------------------------------
1 | export const NEXT_SLIDE = 'NEXT_SLIDE';
2 | export const PREV_SLIDE = 'PREV_SLIDE';
3 |
4 |
--------------------------------------------------------------------------------
/src/slides/bundle1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/bundle1.png
--------------------------------------------------------------------------------
/src/slides/bundle2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/bundle2.png
--------------------------------------------------------------------------------
/src/slides/community.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/community.png
--------------------------------------------------------------------------------
/src/slides/community0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/community0.png
--------------------------------------------------------------------------------
/src/slides/escher.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/escher.jpg
--------------------------------------------------------------------------------
/src/slides/hor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/hor.png
--------------------------------------------------------------------------------
/src/slides/hor2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/hor2.png
--------------------------------------------------------------------------------
/src/slides/hor3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/hor3.png
--------------------------------------------------------------------------------
/src/slides/hor4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/hor4.png
--------------------------------------------------------------------------------
/src/slides/hor5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/hor5.png
--------------------------------------------------------------------------------
/src/slides/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Center from '../components/Center';
3 | import Image from '../components/Image';
4 |
5 | function makeProgression(props, title, items) {
6 | return [
7 | ...items.map((activeItem, activeIndex) =>
8 |
9 | {title}
10 |
11 | {items.map((item, i) =>
12 | -
16 | {item}
17 |
18 | )}
19 |
20 |
21 | )
22 | ];
23 | }
24 |
25 | export default function getSlides() {
26 | return [
27 |
28 |
29 | ,
30 |
31 | Flux + Elm
32 | ,
33 |
34 | 🎂
35 | ,
36 |
37 |
38 | ,
39 |
40 |
41 |
42 |
43 |
3 Million
44 |
45 |
46 | ,
47 |
48 |
49 | ,
50 |
51 | What Made Redux Successful?
52 | ,
53 |
54 | Features and APIs?
55 | ,
56 |
57 | A change emitter holding a value.
58 | ,
59 |
60 |
61 | ,
62 |
63 |
64 | ,
65 |
66 |
67 | ,
68 |
69 |
70 | ,
71 |
72 |
73 | ,
74 |
75 |
76 | ,
77 |
78 |
79 | ,
80 |
81 |
82 | ,
83 |
84 |
85 | ,
86 |
87 | Features & APIs?
88 | ,
89 |
90 | Features & APIs?
91 | ,
92 |
93 |
94 | ,
95 |
96 |
97 | ,
98 |
99 | Constraints & Contracts
100 | ,
101 |
102 | Redux Constraints
103 |
104 | - Single State Tree
105 | - Actions Describe Updates
106 | - Reducers Apply Updates
107 |
108 | ,
109 | ...makeProgression({ alt: true }, 'Debug Workflow', [
110 | 'Log actions and states',
111 | 'Find the bad state',
112 | 'Check the action',
113 | 'Fix the reducer',
114 | 'Write a test'
115 | ]),
116 | ...makeProgression({ alt: true }, 'Everything Is Data', [
117 | 'Persistence',
118 | 'Universal rendering',
119 | 'Recording user sessions',
120 | 'Optimistic mutations',
121 | 'Collaborative editing'
122 | ]),
123 |
128 |
140 |
Features
141 |
142 |
154 |
Constraints
155 |
156 |
,
157 |
158 | Redux Contracts
159 |
160 | - Reducers
161 | - Selectors
162 | - Middleware
163 | - Enhancers
164 |
165 | ,
166 |
167 | App Contracts
168 | ,
169 |
170 | Reducers
171 | (state, action) => state
172 | ,
173 |
174 |
175 | ,
176 |
177 | Selectors
178 | (state, ...args) => derivation
179 | ,
180 |
181 |
182 | ,
183 |
184 | Reducer + Selectors
185 | ,
186 |
187 |
188 | ,
189 |
190 |
191 | ,
192 |
193 | Ecosystem Contracts
194 | ,
195 |
196 | Higher Order Reducers
197 | (reducer, ...args) => reducer
198 | ,
199 |
200 |
201 | ,
202 |
203 |
204 | ,
205 |
206 |
207 | ,
208 |
209 |
210 | ,
211 |
212 |
213 | ,
214 |
215 |
216 | ,
217 |
218 | ♻️
219 | omnidan/
redux-undo
220 | ,
221 |
222 | 😎
223 | mattkrick/
redux-optimistic-ui
224 | ,
225 |
226 | 📝
227 | davidkpiano/
react-redux-form
228 | ,
229 |
230 | Middleware
231 | store => next => action => any
232 | ,
233 |
234 |
235 | ,
236 |
237 |
238 | ,
239 |
240 |
241 | ,
242 |
243 |
244 | ,
245 |
246 |
247 | ,
248 |
249 |
250 | ,
251 |
252 |
253 | ,
254 |
255 |
256 | ,
257 |
258 |
259 | ,
260 |
261 |
262 | ,
263 |
264 | 📰
265 | theaqua/
redux-logger
266 | ,
267 |
268 | 🐥️
269 | redux-observable/
redux-observable
270 | ,
271 |
272 | 🚀
273 | apollostack/
apollo-client
274 | ,
275 |
276 | Enhancers
277 |
278 | createStore => createStore
279 |
280 | ,
281 |
282 | 📦
283 | tappleby/
redux-batched-subscribe
284 | ,
285 |
286 | 🌈
287 | raisemarketplace/
redux-loop
288 | ,
289 |
290 | 🔮
291 | zalmoxisus/
redux-devtools-extension
292 | ,
293 |
298 |
310 |
APIs
311 |
312 |
324 |
Contracts
325 |
326 |
,
327 |
328 | Lessons Learned
329 | ,
330 |
331 | Design Holistically
332 | ,
333 |
339 |
351 |
Features
352 |
353 |
365 |
APIs
366 |
367 |
,
368 |
373 |
385 |
Features
386 |
387 |
399 |
Constraints
400 |
401 |
413 |
Contracts
414 |
415 |
427 |
APIs
428 |
429 |
,
430 |
431 | Make Constraints Useful
432 | ,
433 |
434 | Make Contracts Social
435 | ,
436 |
437 | Design the Way Out
438 | ,
439 |
440 |
441 | ,
442 |
443 | Find the Stress Tests
444 | ,
445 |
446 | Market Good Ideas
447 | ,
448 |
449 | Understanding Matters
450 | ,
451 |
452 | Hype!
453 | ,
454 |
455 | Real Libraries
456 |
457 | - facebook/relay
458 | - reactivex/rxjs
459 | - facebook/react
460 | - mobxjs/mobx
461 | - cerebral/cerebral
462 |
463 | ,
464 |
465 |
466 | ,
467 |
468 |
469 | ,
470 |
471 | Immutable App
Architecture
472 | Lee Byron @ Render 2016
473 | ,
474 |
475 | Cohesive Story
476 |
477 | - Declarative data fetching
478 | - Built-in optimistic mutations
479 | - Easy-to-test async control flow
480 | - Combining local and server data
481 | - Great developer experience
482 |
483 | ,
484 |
485 |
486 | ,
487 |
488 |
489 | ,
490 |
491 | 💜
492 | ,
493 |
494 | P.S.
495 | ,
496 |
497 |
509 |
510 | ];
511 | }
512 |
--------------------------------------------------------------------------------
/src/slides/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/logo.png
--------------------------------------------------------------------------------
/src/slides/middleware.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/middleware.png
--------------------------------------------------------------------------------
/src/slides/middleware2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/middleware2.png
--------------------------------------------------------------------------------
/src/slides/middleware3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/middleware3.png
--------------------------------------------------------------------------------
/src/slides/middleware4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/middleware4.png
--------------------------------------------------------------------------------
/src/slides/middleware5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/middleware5.png
--------------------------------------------------------------------------------
/src/slides/middleware6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/middleware6.png
--------------------------------------------------------------------------------
/src/slides/middleware7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/middleware7.png
--------------------------------------------------------------------------------
/src/slides/middleware8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/middleware8.png
--------------------------------------------------------------------------------
/src/slides/middleware9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/middleware9.png
--------------------------------------------------------------------------------
/src/slides/next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/next.png
--------------------------------------------------------------------------------
/src/slides/npm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/npm.png
--------------------------------------------------------------------------------
/src/slides/reducer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/reducer.png
--------------------------------------------------------------------------------
/src/slides/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { NEXT_SLIDE, PREV_SLIDE } from './actionTypes';
3 | import getSlides from '../slides/index';
4 |
5 | const slideCount = getSlides().length;
6 |
7 | function currentSlide(state = 0, action) {
8 | switch (action.type) {
9 | case NEXT_SLIDE:
10 | return Math.min(slideCount - 1, state + 1);
11 | case PREV_SLIDE:
12 | return Math.max(0, state - 1);
13 | default:
14 | return state;
15 | }
16 | }
17 |
18 | export default combineReducers({
19 | currentSlide
20 | });
--------------------------------------------------------------------------------
/src/slides/selector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/selector.png
--------------------------------------------------------------------------------
/src/slides/setstate2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/setstate2.png
--------------------------------------------------------------------------------
/src/slides/store.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/store.png
--------------------------------------------------------------------------------
/src/slides/store2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/store2.png
--------------------------------------------------------------------------------
/src/slides/store3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/store3.png
--------------------------------------------------------------------------------
/src/slides/store4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/store4.png
--------------------------------------------------------------------------------
/src/slides/store5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/store5.png
--------------------------------------------------------------------------------
/src/slides/store6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/store6.png
--------------------------------------------------------------------------------
/src/slides/store7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/store7.png
--------------------------------------------------------------------------------
/src/slides/store8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/store8.png
--------------------------------------------------------------------------------
/src/slides/tom.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/tom.jpg
--------------------------------------------------------------------------------
/src/slides/twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaearon/the-redux-journey/9c04b2f4baa7e5c1863f0620c3ba31085a991aa6/src/slides/twitter.png
--------------------------------------------------------------------------------
/src/theme.less:
--------------------------------------------------------------------------------
1 | // Default colors
2 | @base00: #181818;
3 | @base01: #282828;
4 | @base02: #383838;
5 | @base03: #585858;
6 | @base04: #b8b8b8;
7 | @base05: #d8d8d8;
8 | @base06: #e8e8e8;
9 | @base07: #f8f8f8;
10 | @base08: #ab4642;
11 | @base09: #dc9656;
12 | @base0A: #f7ca88;
13 | @base0B: #a1b56c;
14 | @base0C: #86c1b9;
15 | @base0D: #7cafc2;
16 | @base0E: #ba8baf;
17 | @base0F: #a16946;
18 |
19 | @font-main: 'Lato', 'LatoBlack', sans-serif;
20 | @font-code: 'Operator Mono', Consolas, monospace;
21 | @fontsize-root: 3.25rem;
22 |
23 | @keyframes shake {
24 | 0%, 100% {transform: translateX(0);}
25 | 20%, 80% {transform: translateX(-0.05rem);}
26 | 40%, 80% {transform: translateX(1.75rem);}
27 | }
28 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'webpack-dev-server/client?http://localhost:3000',
8 | 'webpack/hot/only-dev-server',
9 | './src/index'
10 | ],
11 | output: {
12 | path: path.join(__dirname, 'build'),
13 | filename: 'bundle.js',
14 | publicPath: '/'
15 | },
16 | plugins: [
17 | new webpack.HotModuleReplacementPlugin({ multiStep: true }),
18 | new webpack.NoErrorsPlugin()
19 | ],
20 | resolve: {
21 | extensions: ['', '.js', '.jsx']
22 | },
23 | module: {
24 | loaders: [{
25 | test: /\.jsx?$/,
26 | loaders: ['react-hot', 'babel'],
27 | include: path.join(__dirname, 'src')
28 | }, {
29 | test: /\.css$/,
30 | loaders: ['style', 'css'],
31 | include: path.join(__dirname, 'src')
32 | }, {
33 | test: /\.less$/,
34 | loaders: ['style', 'css', 'less'],
35 | include: path.join(__dirname, 'src')
36 | }, {
37 | test: /\.jpg|png|svg|ico$/,
38 | loaders: ['file-loader'],
39 | include: path.join(__dirname, 'src')
40 | }]
41 | }
42 | };
43 |
--------------------------------------------------------------------------------
/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: [
6 | './src/index'
7 | ],
8 | output: {
9 | path: path.join(__dirname, 'build'),
10 | filename: 'bundle.js',
11 | publicPath: ''
12 | },
13 | plugins: [
14 | new webpack.DefinePlugin({
15 | 'process.env.NODE_ENV': '"production"'
16 | }),
17 | new webpack.optimize.UglifyJsPlugin()
18 | ],
19 | resolve: {
20 | extensions: ['', '.js', '.jsx']
21 | },
22 | module: {
23 | loaders: [{
24 | test: /\.jsx?$/,
25 | loaders: ['babel'],
26 | include: path.join(__dirname, 'src')
27 | }, {
28 | test: /\.css$/,
29 | loaders: ['style', 'css'],
30 | include: path.join(__dirname, 'src')
31 | }, {
32 | test: /\.less$/,
33 | loaders: ['style', 'css', 'less'],
34 | include: path.join(__dirname, 'src')
35 | }, {
36 | test: /\.jpg|png|svg|ico$/,
37 | loaders: ['file-loader?name=[name].[ext]', 'image-webpack-loader'],
38 | include: path.join(__dirname, 'src')
39 | }]
40 | }
41 | };
42 |
--------------------------------------------------------------------------------