понедельник, 29 августа 2016 г.

React Redux Router My CheatSheet

// React Redux Router

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {createStore, combineReducers, applyMiddleware} from 'redux';
import {Provider} from 'react-redux';
import {Router, Route, hashHistory} from 'react-router';
import {syncHistoryWithStore, routerReducer, routerMiddleware, push} from 'react-router-redux';

function reducer (state = {name: 'Boris'}, action) {
    switch (action.type) {
        case 'CHANGE_NAME': return {name: action.name};
        default: return state;
    }
};

const reducers = combineReducers({
      user: reducer
    , routing: routerReducer
});

const initialState = {user: {name: 'Nick'}};

const store = createStore(reducers, applyMiddleware(routerMiddleware(hashHistory)));

const history = syncHistoryWithStore(hashHistory, store);

class App extends Component {
    render () {
        return (
            <div>Привет {store.getState().user.name} <a onClick={this.handleClick.bind(this)}>Измени имя</a> {this.props.children}</div>
        );
    }
    handleClick (event) {
        event.preventDefault();
        store.dispatch({type: 'CHANGE_NAME', name: 'Vova'});
        store.dispatch(push('/change_name'));
    }
}

class List extends Component {
    render () {
        return (
            <div>{store.getState().user.name}</div>
        );
    }
}

ReactDOM.render(
        <Provider store={store}>
            <Router history={history}>
                <Route path="/" component={App}>
                    <Route path="change_name" component={List} />
                </Route>
                <Route path="*" component={App} />
            </Router>
        </Provider>
    , document.getElementById('root')
);

Full React Router My CheatSheet

// Full React Router

import React, {Component, PropTypes} from 'react';
import ReactDOM from 'react-dom';
import {Router, Route, IndexRoute, IndexRedirect, withRouter, hashHistory, Link} from 'react-router';

class NavLink extends Component {
    render () {
        // onlyActiveOnIndex={false} - ссылка всегда выделена при переходе на дочерние адреса
        // onlyActiveOnIndex={true} - с сылки снимается выделение при переходе на дочерине адреса
        return (
            <Link to={this.props.to} onlyActiveOnIndex={this.props.onlyActiveOnIndex} activeClassName="active">{this.props.children}</Link>
        );
    }
}

class App extends Component {
    render () {
        return (
            <div id="main-menu">
                <style>{'.active {font-weight: bold;}'}</style>
                <ul>
                    <li style={{display: 'inline-block'}}><Link to="/page-1" activeClassName="active" onlyActiveOnIndex={true}>Страница 1</Link></li>{' '}
                    <li style={{display: 'inline-block'}}><Link to="/page-2" activeClassName="active" onlyActiveOnIndex={true}>Страница 2</Link></li>{' '}
                    <li style={{display: 'inline-block'}}><NavLink to="/page-3" onlyActiveOnIndex={true}>Страница 3</NavLink></li>
                    <li style={{display: 'inline-block'}}><NavLink to="/form" onlyActiveOnIndex={true}>Форма</NavLink></li>
                </ul>
                {this.props.children}
            </div>
        );
    }
}

class Page1 extends Component {render () {return (<p>Содержимое страницы 1</p>);}}

class Page2 extends Component {
    constructor (props) {
        super(props);
    }
    render () {
        return (<p>Содержимое страницы 2</p>);
    }
    componentDidMount () {
        this.props.router.setRouteLeaveHook(this.props.route, this.routerWillLeave)
    }
    routerWillLeave() {
        let answer = window.confirm('Вы уверены?');
        if (!answer) {return false;}
    }
}

Page2 = withRouter(Page2);

class Page3 extends Component {
    render () {
        return (
            <div>
                <p>Содержимое страницы 3</p>
                <ul>
                    <li style={{display: 'inline-block'}}><Link to="/page-3/section-1/text_for_section_1">Раздел 1</Link></li>{' '}
                    <li style={{display: 'inline-block'}}><Link to="/page-3/section-2/text_for_section_2">Раздел 2</Link></li>
                </ul>
                {this.props.children}
            </div>
        );
    }
}

class Page3Section1 extends Component {render () {return (<p>Секция 1: {this.props.params.text}</p>);}}

class Page3Section2 extends Component {
    render () {
        if (this.props.params.text.replace(/_/g, ' ') === 'text for section 2') {
            return (<p>Секция 2: {this.props.params.text}</p>);
        } else {
            return (<p>Секция 2: текст по умолчанию</p>);
        }
    }
}

class Form extends Component {
    render () {
        return (
            <form onSubmit={this.handleSubmit.bind(this)}>
                <input ref="inp" type="text" value="text_for_section_1" onChange={this.handleOnChange.bind(this)} />
                <input type="submit" value="Отправить" />
            </form>
        );
    }
    handleSubmit (event) {
        event.preventDefault();
        this.context.router.push(`/page-3/section-1/${this.refs.inp.value}`); // Redirect
    }
    handleOnChange () {}
}

Form.contextTypes = {
    router: PropTypes.object.isRequired
};

class NotFound extends Component {render () {return (<p>Страница не найдена. <Link to="/page-1">Вернуться на главную.</Link></p>);}}

function sayGoodbye () {console.log('Goodbye');}
Page2.sayHello = function () {console.log('Hello');}

let routes = (
    <div>
          {/* полностью перерисовать компонент App и всё его содержимое при абсолютно любом переходе */}
          <Route path="/" component={App}>
              {/*<IndexRoute component={Page1} />*/}
              <IndexRedirect to="page-2" />
              <Route path="page-1" component={Page1} onLeave={sayGoodbye} />
              <Route path="page-2" component={Page2} onEnter={Page2.sayHello} />
              <Route path="page-3" component={Page3}>
                  <Route path="section-1/:text" component={Page3Section1} />
                  <Route path="section-2/:text" component={Page3Section2} />
              </Route>
              <Route path="form" component={Form} />
          </Route>
          <Route path='*' component={NotFound} />
    </div>
);

ReactDOM.render(<Router history={hashHistory} routes={routes} />, document.getElementById('root'));

Simple React Router My CheatSheet

// Simple React Router

import React, {Component} from 'react';
import ReactDOM from 'react-dom';

class App extends Component {
    constructor (props) {
        super(props);
        this.state = {
            route: window.location.hash.substr(1)
        };
    }
    render () {
        let Page;
        switch (this.state.route) {
            case '/page-1': Page = <p>Содержимое страницы 1</p>; break;
            case '/page-2': Page = <p>Содержимое страницы 2</p>; break;
            case '/page-3': Page = <p>Содержимое страницы 3</p>; break;
            default: Page = <p>Содержимое страницы 1</p>;
        }
        return (
            <div id="main-menu">
                <ul>
                    <li style={{display: 'inline-block'}}><a href="#/page-1">Страница 1</a></li>{' '}
                    <li style={{display: 'inline-block'}}><a href="#/page-2">Страница 2</a></li>{' '}
                    <li style={{display: 'inline-block'}}><a href="#/page-3">Страница 3</a></li>
                </ul>
                {Page}
            </div>
        );
    }
    componentDidMount () {
        window.addEventListener('hashchange', () => {
            this.setState({route: window.location.hash.substr(1)});
        });
    }
}

ReactDOM.render(<App />, document.getElementById('root'));

пятница, 26 августа 2016 г.

React-Redux My CheatSheet

// React Redux

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {createStore, combineReducers, applyMiddleware, bindActionCreators} from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import {Provider, connect} from 'react-redux';

function counterUpReducer (state = 0, action) {
    switch (action.type) {
        case 'COUNTER_UP': return state + action.value;
        default: return state;
    }
}

function counterUpAction (value) {
    return {
          type: 'COUNTER_UP'
        , value: value
    };
}

function counterDownReducer (state = 0, action) {
    switch (action.type) {
        case 'COUNTER_DOWN': return state + action.value;
        default: return state;
    }
}

function counterDownAction (value) {
    return {
          type: 'COUNTER_DOWN'
        , value: value
    };
}

const reducers = combineReducers({
      counterUp: counterUpReducer
    , counterDown: counterDownReducer
});

const initialState = {
      counterUp: 0
    , counterDown: 0
};

const store = createStore(reducers, initialState, applyMiddleware(logger(), thunk));

store.subscribe(() => {console.log(store.getState());});

class App extends Component {
    render () {
        return (
            <CounterBlock />
        );
    }
}

class CounterBlock extends Component {
    render () {
        return (
            <Counter />
        );
    }
}

class Counter extends Component {
    constructor (props) {
        super(props);
        this.state = store.getState();
    }
    render () {
        return (
            <div id="counter">
                <span id="counter-up">{this.props.counterUp}</span>
                {' '}
                <span id="up" onClick={this.props.handleUpClick.bind(this)}>Вверх</span>
                <br /><br />
                <span id="counter-down">{this.props.counterDown}</span>
                {' '}
                <span id="down" onClick={this.props.handleDownClick.bind(this)}>Вниз</span>
            </div>
        );
    }
}

function mapStateToProps (state) {console.log(state.counterUp);
    return {
          counterUp: state.counterUp
        , counterDown: state.counterDown
    };
}

function mapDispatchToProps (dispatch) {
    return {
          handleUpClick: function () {
            dispatch(counterUpAction(1));
            this.setState(Object.assign({}, this.state, {counterUp: store.getState().counterUp}));
          }
        , handleDownClick: function () {
            dispatch(counterDownAction(-1));
            this.setState(Object.assign({}, this.state, {counterDown: store.getState().counterDown}));
          }
    };
}

Counter = connect(mapStateToProps, mapDispatchToProps)(Counter);

ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));

Redux My CheatSheet

// Redux

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {createStore, combineReducers, applyMiddleware, bindActionCreators} from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';

function counterUpReducer (state = 0, action) {
    switch (action.type) {
        case 'COUNTER_UP': return state + action.value;
        default: return state;
    }
}

function counterUpAction (value) {
    return {
          type: 'COUNTER_UP'
        , value: value
    };
}

function asyncCounterUpAction (value, setState) {
    return (dispatch) => {
        setTimeout(function () {
            dispatch(counterUpAction(value));
        }, 1000);
    };
}

function counterDownReducer (state = 0, action) {
    switch (action.type) {
        case 'COUNTER_DOWN': return state + action.value;
        default: return state;
    }
}

function counterDownAction (value) {
    return {
          type: 'COUNTER_DOWN'
        , value: value
    };
}

function asyncCounterDownAction (value) {
    return (dispatch) => {
        setTimeout(function () {
            dispatch(counterDownAction(value));
        }, 1000);
    };
}

const reducers = combineReducers({
      counterUp: counterUpReducer
    , counterDown: counterDownReducer
});

const initialState = {
      counterUp: 0
    , counterDown: 0
};

const store = createStore(reducers, initialState, applyMiddleware(logger(), thunk));

store.subscribe(() => {console.log(store.getState());});

const actionCreators = {
      boundCounterUpAction: counterUpAction
    , boundCounterDownAction: counterDownAction
};

const boundActions = bindActionCreators(actionCreators, store.dispatch);

class App extends Component {
    render () {
        return (
            <CounterBlock
                                  counterUpAction={counterUpAction}
                                  counterDownAction={counterDownAction}
                                  boundActions={boundActions}
                                  asyncCounterUpAction={asyncCounterUpAction}
                                  asyncCounterDownAction={asyncCounterDownAction}
            />
        );
    }
}

class CounterBlock extends Component {
    render () {
        return (
            <Counter
                          counterUpAction={this.props.counterUpAction}
                          counterDownAction={this.props.counterDownAction}
                          boundActions={this.props.boundActions}
                          asyncCounterUpAction={this.props.asyncCounterUpAction}
                          asyncCounterDownAction={this.props.asyncCounterDownAction}
            />
        );
    }
}

class Counter extends Component {
    constructor (props) {
        super(props);
        this.state = store.getState();
    }
    render () {
        return (
            <div id="counter">
                <span id="counter-up">{this.state.counterUp}</span>
                {' '}
                <span id="up" onClick={this.handleUpClick.bind(this)}>Вверх</span>
                {' '}
                <span id="up-bound" onClick={this.handleBoundUpClick.bind(this)}>Вверх связанный</span>
                {' '}
                <span id="up-async" onClick={this.handleAsyncUpClick.bind(this)}>Вверх асинхронный</span>
                <br /><br />
                <span id="counter-down">{this.state.counterDown}</span>
                {' '}
                <span id="down" onClick={this.handleDownClick.bind(this)}>Вниз</span>
                {' '}
                <span id="down-bound" onClick={this.handleBoundDownClick.bind(this)}>Вниз связанный</span>
                {' '}
                <span id="down-async" onClick={this.handleAsyncDownClick.bind(this)}>Вниз асинхронный</span>
            </div>
        );
    }
    handleUpClick () {
        store.dispatch(this.props.counterUpAction(1));
        this.setState(Object.assign({}, this.state, {counterUp: store.getState().counterUp}));
    }
    handleDownClick () {
        store.dispatch(this.props.counterDownAction(-1));
        this.setState(Object.assign({}, this.state, {counterDown: store.getState().counterDown}));
    }
    handleBoundUpClick () {
        this.props.boundActions.boundCounterUpAction(1);
        this.setState(Object.assign({}, this.state, {counterUp: store.getState().counterUp}));
    }
    handleBoundDownClick () {
        this.props.boundActions.boundCounterDownAction(-1);
        this.setState(Object.assign({}, this.state, {counterDown: store.getState().counterDown}));
    }
    handleAsyncUpClick () {
        store.dispatch(this.props.asyncCounterUpAction(1));
        setTimeout(() => {
            this.setState(Object.assign({}, this.state, {counterUp: store.getState().counterUp}));
        }, 2000);
    }
    handleAsyncDownClick () {
        store.dispatch(this.props.asyncCounterDownAction(-1));
        setTimeout(() => {
            this.setState(Object.assign({}, this.state, {counterDown: store.getState().counterDown}));
        }, 2000);
    }
}

ReactDOM.render(<App />, document.getElementById('root'));

четверг, 25 августа 2016 г.

React My CheatSheet

// React

import React, {Component, PropTypes} from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(<App />, document.getElementById('root'));

class App extends Component {
    render () {
        return (
            {/* Это комментарий к компоненту */}
            <div id="app"> // а это комментарий в конце строки
                <p>{     }Так{     }вставляется{     }множество{     }пробелов{     }</p>
                <Header identificator="header" func={function () {console.log('OK');}}>Главное меню</Header>
                <DangerousHTML />
                <Menu />
                <Form />
                <PropsValidation />
                <Lifecycle />
            </div>
        );
    }
}

class Header extends Component {
    render () {
        this.props.func();
        <h1 className={this.props.identificator === 'header' ? 'bold' : 'thin'}
               style={{backgroundColor: 'red', color: 'blue'}}
               data-custom-attribute="foo"
        >this.props.children</h1>
    }
}

class DangerousHTML extends Component {
    render () {
        return (
            <div dangerouslySetInnerHTML={{__html: '<span>&middot; Это опасный HTML-код</span>'}}></div>
        );
    }
}

class Menu extends Component {
    render () {
        const elements = [
              ['главная', '/']
            , ['отчеты', '/reports']
            , ['контакты', '/contacts']
        ];
        return (
            <List elementsArray={elements} />
        );
    }
}

class List extends Component {
    render () {
        <ul>
            {this.props.elementsArray.map((element, index) => {
                return (
                    <li key={index}><a href={element[1]} onClick={this.handleClick.bind(this)}>{element[0]}</a></li>
                );
            })}
        </ul>
    }
    handelClick (event) {
        event.preventDefault();
        console.log(event.target.innerHTML);
    }
}

class Form extends Component {
    constructor (props) {
        super(props);
        this.state = {
              inputValue: 'Анна'
            , textareaValue: 'Пришла на работу'
            , selectValue: 'one'
            , checkboxValue: true
        };
    }
    render () {
        return (
            <form method="get" action="/" onSubmit={this.handleSubmit.bind(this)}>
                <label htmlFor="input">Имя</label>
                <input ref="inputRef" type="text" disabled={false} defaultValue="Борис" value={this.state.inputValue} onChange={this.handleInputChange.bind(this)} />
                <textarea ref="textareaRef" disabled={false} defaultValue="Идет домой" value={this.state.textareaValue} onChange={this.handleTextareaChange.bind(this)} />
                <select ref="selectRef" disabled={false} multiple={true} value={selectValue} {/* или value={[selectValue, 'two']} */}  onChange={this.handleSelectChange.bind(this)}>
                    <option value="one">один</option>
                    <option value="two">два</option>
                    <option value="three">три</option>
                </select>
                <input ref="checkboxRef" type="checkbox" disabled={false} checked={this.props.checkboxValue} onChange={this.handleCheckboxChange.bind(this)} />{' '}Всё разрешено
                <input type="submit" value="Найти" />
                <input type="clear" value="Очистить" />
            </form>
        );
    }
    handleInputChange (event) {
        this.setState(Object.assing({}, this.state, {inputValue: event.target.value > 5 ? event.target.value.slice(0, 5) : event.target.value}));
    }
    handleTextareaChange (event) {
        this.setState(Object.assing({}, this.state, {textareaValue: event.target.value}));
    }
    handleSelectChange (event) {
        console.log(event.target.value);
    }
    handleCheckboxChange (event) {
        this.setState(Object.assing({}, this.state, {checkboxValue: event.target.checked === true ? 'checked' : ''}));
    }
    handleSubmit (event) {
        event.preventDefault();
        $.ajax({
              url: '/send'
            , type: 'POST'
            , data: {
                  input: this.refs.inputRef.value
                , textarea: this.refs.textareaRef.value
                , select this.refs.selectRef.value
                , checkbox: this.refs.checkboxRef.checked
              }
            , dataType: 'json'
            , success: function () {
                this.setState(Object.assign({}, this.state, {
                      inputValue: ''
                    , textareaValue: ''
                    , selectValue: ''
                    , checkboxValue: false
                }));
              }.bind(this)
            , error: function () {}.bind(this)
        });
    }
    handleClear (event) {
        event.preventDefault();
        this.state = {
              inputValue: ''
            , textareaValue: ''
            , selectValue: ''
            , checkboxValue: true
        };
    }
}

class PropsValidation extends Component {
    static defaultProps = {
          optionalBoolean: true
        , optionalNumber: 1
        , optionalString: 'text'
    }
    static propTypes = {
          optionalBoolean: PropTypes.bool

        , optionalNumber: PropTypes.number

        , optionalString: PropTypes.string
        , optionalOneOf: PropTypes.oneOf(['News', 'Photos'])

        , optionalArray: PropTypes.array
        , optionalArrayOf: PropTypes.arrayOf(PropTypes.number)

        , optionalObject: PropTypes.object
        , optionalObjectOf: PropTypes.objectOf(PropTypes.number)
        , optionalObjectWithShape: PropTypes.shape({color: PropTypes.string, fontSize: PropTypes.number})

        , optionalFunction: PropTypes.func
        , requiredFunction: PropTypes.func.isRequired

        , optionalSymbol: PropTypes.symbol

        , optionalReactElement: PropTypes.element

        , anythingOptionalThatCanBeRendered: PropTypes.node

        , optionalInstanceOfClass: PropTypes.instanceOf(Array)

        , optionalOneOfType: PropTypes.oneOfType([PropTypes.number, PropTypes.string])

        , requiredAny: PropTypes.any.isRequired

        , customProp: function (props, propName, componentName) {
            if (!/matchme/.test(props[propName])) {
                return new Error('Invalid prop "' + propName + '" supplied to "' + componentName + '". Validation failed.');
            }
        }
    }
    render () {
        return (
            <div>
                {this.props.optionalBoolean}
                {this.props.optionalNumber}
                {this.props.optionalString}
                {this.props.optionalOneOf}
                {this.props.optionalArray}
                {this.props.optionalArrayOf}
                {this.props.optionalObject}
                {this.props.optionalObjectOf}
                {this.props.optionalObjectWithShape}
                {this.props.optionalFunction()}
                {this.props.requiredFunction()}
                {this.props.optionalSymbol}
                {this.props.optionalReactElement}
                {this.props.anythingOptionalThatCanBeRendered}
                {this.props.optionalInstanceOfClass}
                {this.props.optionalOneOfType}
                {this.props.requiredAny}
                {this.props.customProp}
            </div>
        );
    }
}

class Lifecycle extends Component {
    /* componentWillMount (props) { */ constructor (props) { // Установка первоначальных значений - выполняется 1 раз перед вставкой компонента на страницу
        super(props);
        /* getInitialState () {return {comments: []};} */ this.state = {comments: []};
        this.intervals = [];
    }
    setInterval: function() {
        this.intervals.push(setInterval.apply(null, arguments));
    }
    render () { // Вызывается при вставке компонента на страницу и каждый раз при изменении свойств this.props и состояния через this.setState()
        if (this.state.comments.length > 0) {
            return (
                <ul className="comments">
                    {this.state.comments.map((comment) => {
                        return (
                            <li>comment</li>
                        );
                    })}
                </ul>
            );
        } else {
            return (
                <p>Нет данных</p>
            );
        }
    }
    componentDidMount () { // Вызывается только 1 раз после того, как компонент будет вставлен на страницу - можно изменять рамеры элементов после вставки компонента или для получения данных через AJAX
        getData().then((data) => {
            this.setState({comments: data});
        });
        resizeComponent();
        this.setInterval(this.tick, 1000);
    }
    componentWillReceiveProps (nextProps) { // Вызывается, когда уже вставленный на страницу компонент перезагружается и получает новые свойства props - обычно не нужно менять
        this.setState(Object.assign({}, this.state, {comments: nextProps.comments}));
    }
    shouldComponentUpdate (nextProps, nextState) { // Вызывается каждый раз перед перерисовкой компонента - обычно не нужно менять
        // return nextProps.id !== this.props.id;
        if (this.state.comments.length > 0) {
            return true;
        } else {
            return false;
        }
    }
    componentWillUpdate () { // Вызывается многократно перед обновлением содержимого компонента на странице - можно устанавливать слушатели событий
        console.log('All data is valid');
    }
    componentDidUpdate () { // Вызывается многократно после обновления содержимого компонента на странице - можно изменять рамеры элементов после вставки компонента
        resizeComponent();
    }
    componentWillUnmount () { // Удаление слушаетелей событий перед удалением компонента из содержимого страницы
        this.intervals.forEach(clearInterval);
    }
}

среда, 17 августа 2016 г.

Redux Simple functions

let listeners = [];
let state;

function subscribe (listener) {
    listeners.push(listener);
    return function () {
        listeners = listeners.filter(listenerInside => listenerInside !== listener);
    }
}

funciton dispatch (action) {
    state = reducer(state, action);
    listeners.slice().forEach(listener => listener());
    return action;
}

function getState () {
    return state;
}

вторник, 9 августа 2016 г.

React Redux Webpack - очень простой пример

package.json

{
      "version": "1.0.0"
    , "name": "react-redux-webpack"
    , "description": "React Redux Webpack"
    , "engines": {
          "node": "6.3.1"
        , "npm": "3.10.6"
      }
    , "dependencies": {
          "webpack": "*"
        , "webpack-dev-server": "*"
        , "babel-core": "*"
        , "babel-preset-es2015": "*"
        , "babel-preset-react": "*"
        , "babel-loader": "*"
        , "react": "*"
        , "react-dom": "*"
        , "redux": "*"
      }
    , "scripts": {
          "build": "node node_modules/webpack-dev-server/bin/webpack-dev-server --progress"
      }
}

webpack.config.js

var path = require('path');

module.exports = {
      entry: [path.resolve(__dirname + '/app.jsx')]
    , output: {
            path: path.resolve(__dirname)
          , filename: 'bundle.js'
      }
    , module: {
         loaders: [
            {
                  test: /\.jsx?$/
                , exclude: /node_modules/
                , loader: 'babel-loader'
                , query: {
                    presets: ['es2015', 'react']
                  }
            }
         ]
      }
};

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Redux + React</title>
</head>
<body>
    <div id="root"></div>
    <script src="bundle.js"></script>
</body>
</html>

app.jsx

import React, {Component} from 'react';
import ReactDOM from 'react-dom';

import {createStore} from 'redux';

const reducer = (state = {say: 'Hi'}, action) => {
    switch (action.type) {
        case 'SAY OK': return {say: 'OK'};
        default: return state;
    }
}

const store = createStore(reducer);

class HelloWorld extends Component {
    constructor () {
        super(...arguments);
        this.state = store.getState();
        this.unsubscribe = store.subscribe(() => {
            this.setState(store.getState());
        });
    }
    sayOK () {
        store.dispatch({type: 'SAY OK'});
    }
    render () {
        return (
            <div>
                <h1>{this.state.say}</h1>
                <h2 onClick={this.sayOK.bind(this)}>Скажи ОК</h2>
            </div>
        );
    }
}

ReactDOM.render(<HelloWorld />, document.getElementById('root'));

Webpack Dev server and Express

var Express = require('express');
var webpack = require('webpack');

var config = require('../src/config');
var webpackConfig = require('./dev.config');
var compiler = webpack(webpackConfig);

var host = config.host || 'localhost';
var port = (Number(config.port) + 1) || 3001;
var serverOptions = {
  contentBase: 'http://' + host + ':' + port,
  quiet: true,
  noInfo: true,
  hot: true,
  inline: true,
  lazy: false,
  publicPath: webpackConfig.output.publicPath,
  headers: {'Access-Control-Allow-Origin': '*'},
  stats: {colors: true}
};

var app = new Express();

app.use(require('webpack-dev-middleware')(compiler, serverOptions));
app.use(require('webpack-hot-middleware')(compiler));

app.listen(port, function onAppListening(err) {
  if (err) {
    console.error(err);
  } else {
    console.info('==> 🚧  Webpack development server listening on port %s', port);
  }
});

Redux Tutorial



Redux - Hold and update the entire state of the app in the simplest manner possible while also using the least amount of boilerplate code.

Actions - Pure object that is returned by a pure function with no side-effects. The action object contains the “type” and any necessary information to perform it. It does not actually do anything, it just defines the action and passes the necessary data. The object is sent to the store using dispatch(). Actions describe that something happened.

Reducer - A pure function that takes the current state and action, and performs the update on the state. It returns the new state. Typically use a switch statement that reads the action.type and then creates the new state this action.

Store - There is only one and it holds the entire state in a single object. Assign it to a variable using createStore(combinedReducer). The store passes two arguments to the reducer.

Flow:
1. Interaction requires an update
2. Call store.dispatch(action)
3. Redux store calls the reducer
4. The root reducer may combine the output of multiple reducers into a single state tree
5. The Redux store saves the complete state tree returned by the root reducer
6. The components are "subscribed" through the connect function and updated accordingly

github.com/happypoulp/redux-tutorial

// Tutorial 0 - introduction.js

// Why this tutorial?
// While trying to learn Redux, I realized that I had accumulated incorrect knowledge about flux through
// articles I read and personal experience. I don't mean that articles about flux are not well written
// but I just didn't grasp concepts correctly. In the end, I was just applying documentation of different
// flux frameworks (Reflux, Flummox, FB Flux) and trying to make them match with the theoretical concept I read
// about (actions / actions creators, store, dispatcher, etc).
// Only when I started using Redux did I realize that flux is more simple than I thought. This is all
// thanks to Redux being very well designed and having removed a lot of "anti-boilerplate features" introduced
// by other frameworks I tried before. I now feel that Redux is a much better way to learn about flux
// than many other frameworks. That's why I want now to share with everyone, using my own words,
// flux concepts that I am starting to grasp, focusing on the use of Redux.

// You may have seen this diagram representing the famous unidirectional data flow of a flux application:

/*
                 _________               ____________               ___________
                |         |             |            |             |           |
                | Action  |------------▶| Dispatcher |------------▶| callbacks |
                |_________|             |____________|             |___________|
                     ▲                                                   |
                     |                                                   |
                     |                                                   |
 _________       ____|_____                                          ____▼____
|         |◀----|  Action  |                                        |         |
| Web API |     | Creators |                                        |  Store  |
|_________|----▶|__________|                                        |_________|
                     ▲                                                   |
                     |                                                   |
                 ____|________           ____________                ____▼____
                |   User       |         |   React   |              | Change  |
                | interactions |◀--------|   Views   |◀-------------| events  |
                |______________|         |___________|              |_________|

*/

// In this tutorial we'll gradually introduce you to concepts of the diagram above. But instead of trying
// to explain this complete diagram and the overall flow it describes, we'll take each piece separately and try to
// understand why it exists and what role it plays. In the end you'll see that this diagram makes perfect sense
// once we understand each of its parts.

// But before we start, let's talk a little bit about why flux exists and why we need it...
// Let's pretend we're building a web application. What are all web applications made of?
// 1) Templates / html = View
// 2) Data that will populate our views = Models
// 3) Logic to retrieve data, glue all views together and to react accordingly to user events,
//    data modifications, etc. = Controller

// This is the very classic MVC that we all know about. But it actually looks like concepts of flux,
// just expressed in a slightly different way:
// - Models look like stores
// - user events, data modifications and their handlers look like
//   "action creators" -> action -> dispatcher -> callback
// - Views look like React views (or anything else as far as flux is concerned)

// So is flux just a matter of new vocabulary? Not exactly. But vocabulary DOES matter, because by introducing
// these new terms we are now able to express more precisely things that were regrouped under
// various terminologies... For example, isn't a data fetch an action? Just like a click is also an action?
// And a change in an input is an action too... Then we're all already used to issuing actions from our
// applications, we were just calling them differently. And instead of having handlers for those
// actions directly modify Models or Views, flux ensures all actions go first through something called
// a dispatcher, then through our stores, and finally all watchers of stores are notified.

// To get more clarity how MVC and flux differ, we'll
// take a classic use-case in an MVC application:
// In a classic MVC application you could easily end up with:
// 1) User clicks on button "A"
// 2) A click handler on button "A" triggers a change on Model "A"
// 3) A change handler on Model "A" triggers a change on Model "B"
// 4) A change handler on Model "B" triggers a change on View "B" that re-renders itself

// Finding the source of a bug in such an environment when something goes wrong can become quite challenging
// very quickly. This is because every View can watch every Model, and every Model can watch other Models, so
// basically data can arrive from a lot of places and be changed by a lot of sources (any views or any models).

// Whereas when using flux and its unidirectional data flow, the example above could become:
// 1) user clicks on button "A"
// 2) a handler on button "A" triggers an action that is dispatched and produces a change on Store "A"
// 3) since all other stores are also notified about the action, Store B can react to the same action too
// 4) View "B" gets notified by the change in Stores A and B, and re-renders

// See how we avoid directly linking Store A to Store B? Each store can only be
// modified by an action and nothing else. And once all stores have replied to an action,
// views can finally update. So in the end, data always flows in one way:
//     action -> store -> view -> action -> store -> view -> action -> ...

// Just as we started our use case above from an action, let's start our tutorial with
// actions and action creators.

// Go to next tutorial: 01_simple-action-creator.js

// Tutorial 1 - simple-action-creator.js

// We started to talk a little about actions in the introduction but what exactly are those "action creators"
// and how are they linked to "actions"?

// It's actually so simple that a few lines of code can explain it all!

// The action creator is just a function...
var actionCreator = function() {
    // ...that creates an action (yeah, the name action creator is pretty obvious now) and returns it
    return {
        type: 'AN_ACTION'
    }
}

// So is that all? yes.

// However, one thing to note is the format of the action. This is kind of a convention in flux
// that the action is an object that contains a "type" property. This type allows for further
// handling of the action. Of course, the action can also contain other properties to
// pass any data you want.

// We'll also see later that the action creator can actually return something other than an action,
// like a function. This will be extremely useful for async action handling (more on that
// in dispatch-async-action.js).

// We can call this action creator and get an action as expected:
console.log(actionCreator())
// Output: { type: 'AN_ACTION' }

// Ok, this works but it does not go anywhere...
// What we need is to have this action be sent somewhere so that
// anyone interested could know that something happened and could act accordingly.
// We call this process "Dispatching an action".

// To dispatch an action we need... a dispatch function ("Captain obvious").
// And to let anyone interested know that an action happened, we need a mechanism to register
// "handlers". Such "handlers" to actions in traditional flux application are called stores and
// we'll see in the next section how they are called in redux.

// So far here is the flow of our application:
// ActionCreator -> Action

// Read more about actions and action creators here:
// http://rackt.org/redux/docs/recipes/ReducingBoilerplate.html

// Go to next tutorial: 02_about-state-and-meet-redux.js

// Tutorial 02 - about-state-and-meet-redux.js

// Sometimes the actions that we'll handle in our application will not only inform us
// that something happened but also tell us that data needs to be updated.

// This is actually quite a big challenge in any app.
// Where do I keep all the data regarding my application along its lifetime?
// How do I handle modification of such data?
// How do I propagate modifications to all parts of my application?

// Here comes Redux.

// Redux (https://github.com/rackt/redux) is a "predictable state container for JavaScript apps"

// Let's review the above questions and reply to them with
// Redux vocabulary (flux vocabulary too for some of them):

// Where do I keep all the data regarding my application along its lifetime?
//     You keep it the way you want (JS object, array, Immutable structure, ...).
//     Data of your application will be called state. This makes sense since we're talking about
//     all the application's data that will evolve over time, it's really the application's state.
//     But you hand it over to Redux (Redux is a "state container", remember?).
// How do I handle data modifications?
//     Using reducers (called "stores" in traditional flux).
//     A reducer is a subscriber to actions.
//     A reducer is just a function that receives the current state of your application, the action,
//     and returns a new state modified (or reduced as they call it)
// How do I propagate modifications to all parts of my application?
//     Using subscribers to state's modifications.

// Redux ties all this together for you.
// To sum up, Redux will provide you:
//     1) a place to put your application state
//     2) a mechanism to dispatch actions to modifiers of your application state, AKA reducers
//     3) a mechanism to subscribe to state updates

// The Redux instance is called a store and can be created like this:
/*
    import { createStore } from 'redux'
    var store = createStore()
*/

// But if you run the code above, you'll notice that it throws an error:
//     Error: Invariant Violation: Expected the reducer to be a function.

// That's because createStore expects a function that will allow it to reduce your state.

// Let's try again

import { createStore } from 'redux'

var store = createStore(() => {})

// Seems good for now...

// Go to next tutorial: 03_simple-reducer.js

// Tutorial 03 - simple-reducer.js

// Now that we know how to create a Redux instance that will hold the state of our application
// we will focus on those reducer functions that will allow us to transform this state.

// A word about reducer VS store:
// As you may have noticed, in the flux diagram shown in the introduction, we had "Store", not
// "Reducer" like Redux is expecting. So how exactly do Store and Reducer differ?
// It's more simple than you could imagine: A Store keeps your data in it while a Reducer doesn't.
// So in traditional flux, stores hold state in them while in Redux, each time a reducer is
// called, it is passed the state that needs to be updated. This way, Redux's stores became
// "stateless stores" and were renamed reducers.

// As stated before, when creating a Redux instance you must give it a reducer function...

import { createStore } from 'redux'

var store_0 = createStore(() => {})

// ... so that Redux can call this function on your application state each time an action occurs.
// Giving reducer(s) to createStore is exactly how redux registers the action "handlers" (read reducers) we
// were talking about in section 01_simple-action-creator.js.

// Let's put some log in our reducer

var reducer = function (...args) {
    console.log('Reducer was called with args', args)
}

var store_1 = createStore(reducer)

// Output: Reducer was called with args [ undefined, { type: '@@redux/INIT' } ]

// Did you see that? Our reducer is actually called even if we didn't dispatch any action...
// That's because to initialize the state of the application,
// Redux actually dispatches an init action ({ type: '@@redux/INIT' })

// When called, a reducer is given those parameters: (state, action)
// It's then very logical that at an application initialization, the state, not being
// initialized yet, is "undefined"

// But then what is the state of our application after Redux sends its "init" action?

// Go to next tutorial: 04_get-state.js

// Tutorial 04 - get-state.js

// How do we retrieve the state from our Redux instance?

import { createStore } from 'redux'

var reducer_0 = function (state, action) {
    console.log('reducer_0 was called with state', state, 'and action', action)
}

var store_0 = createStore(reducer_0)
// Output: reducer_0 was called with state undefined and action { type: '@@redux/INIT' }

// To get the state that Redux is holding for us, you call getState

console.log('store_0 state after initialization:', store_0.getState())
// Output: store_0 state after initialization: undefined

// So the state of our application is still undefined after the initialization? Well of course it is,
// our reducer is not doing anything... Remember how we described the expected behavior of a reducer in
// "about-state-and-meet-redux"?
//     "A reducer is just a function that receives the current state of your application, the action,
//     and returns a new state modified (or reduced as they call it)"
// Our reducer is not returning anything right now so the state of our application is what
// reducer() returns, hence "undefined".

// Let's try to send an initial state of our application if the state given to reducer is undefined:

var reducer_1 = function (state, action) {
    console.log('reducer_1 was called with state', state, 'and action', action)
    if (typeof state === 'undefined') {
        return {}
    }

    return state;
}

var store_1 = createStore(reducer_1)
// Output: reducer_1 was called with state undefined and action { type: '@@redux/INIT' }

console.log('store_1 state after initialization:', store_1.getState())
// Output: store_1 state after initialization: {}

// As expected, the state returned by Redux after initialization is now {}

// There is however a much cleaner way to implement this pattern thanks to ES6:

var reducer_2 = function (state = {}, action) {
    console.log('reducer_2 was called with state', state, 'and action', action)

    return state;
}

var store_2 = createStore(reducer_2)
// Output: reducer_2 was called with state {} and action { type: '@@redux/INIT' }

console.log('store_2 state after initialization:', store_2.getState())
// Output: store_2 state after initialization: {}

// You've probably noticed that since we've used the default parameter on state parameter of reducer_2,
// we no longer get undefined as state's value in our reducer's body.

// Let's now recall that a reducer is only called in response to an action dispatched and
// let's fake a state modification in response to an action type 'SAY_SOMETHING'

var reducer_3 = function (state = {}, action) {
    console.log('reducer_3 was called with state', state, 'and action', action)

    switch (action.type) {
        case 'SAY_SOMETHING':
            return {
                ...state,
                message: action.value
            }
        default:
            return state;
    }
}

var store_3 = createStore(reducer_3)
// Output: reducer_3 was called with state {} and action { type: '@@redux/INIT' }

console.log('store_3 state after initialization:', store_3.getState())
// Output: redux state after initialization: {}

// Nothing new in our state so far since we did not dispatch any action yet. But there are few
// important things to pay attention to in the last example:
//     0) I assumed that our action contains a type and a value property. The type property is mostly
//        a convention in flux actions and the value property could have been anything else.
//     1) You'll often see the pattern involving a switch to respond appropriately
//        to an action received in your reducers
//     2) When using a switch, NEVER forget to have a "default: return state" because
//        if you don't, you'll end up having your reducer return undefined (and lose your state).
//     3) Notice how we returned a new state made by merging current state with { message: action.value },
//        all that thanks to this awesome ES7 notation (Object Spread): { ...state, message: action.value }
//     4) Note also that this ES7 Object Spread notation suits our example because it's doing a shallow
//        copy of { message: action.value } over our state (meaning that first level properties of state
//        are completely overwritten - as opposed to gracefully merged - by first level property of
//        { message: action.value }). But if we had a more complex / nested data structure, you might choose
//        to handle your state's updates very differently:
//        - using Immutable.js (https://facebook.github.io/immutable-js/)
//        - using Object.assign (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
//        - using manual merge
//        - or whatever other strategy that suits your needs and the structure of your state since
//          Redux is absolutely NOT opinionated on this (remember, Redux is a state container).

// Now that we're starting to handle actions in our reducer let's talk about having multiple reducers and
// combining them.

// Go to next tutorial: 05_combine-reducers.js

// Tutorial 05 - combine-reducers.js

// We're now starting to get a grasp of what a reducer is...

var reducer_0 = function (state = {}, action) {
    console.log('reducer_0 was called with state', state, 'and action', action)

    switch (action.type) {
        case 'SAY_SOMETHING':
            return {
                ...state,
                message: action.value
            }
        default:
            return state;
    }
}

// ... but before going further, we should start wondering what our reducer will look like when
// we'll have tens of actions:

var reducer_1 = function (state = {}, action) {
    console.log('reducer_1 was called with state', state, 'and action', action)

    switch (action.type) {
        case 'SAY_SOMETHING':
            return {
                ...state,
                message: action.value
            }
        case 'DO_SOMETHING':
            // ...
        case 'LEARN_SOMETHING':
            // ...
        case 'HEAR_SOMETHING':
            // ...
        case 'GO_SOMEWHERE':
            // ...
        // etc.
        default:
            return state;
    }
}

// It becomes quite evident that a single reducer function cannot hold all our
// application's actions handling (well it could hold it, but it wouldn't be very maintainable...).

// Luckily for us, Redux doesn't care if we have one reducer or a dozen and it will even help us to
// combine them if we have many!

// Let's declare 2 reducers

var userReducer = function (state = {}, action) {
    console.log('userReducer was called with state', state, 'and action', action)

    switch (action.type) {
        // etc.
        default:
            return state;
    }
}
var itemsReducer = function (state = [], action) {
    console.log('itemsReducer was called with state', state, 'and action', action)

    switch (action.type) {
        // etc.
        default:
            return state;
    }
}

// I'd like you to pay special attention to the initial state that was actually given to
// each reducer: userReducer got an initial state in the form of a literal object ({}) while
// itemsReducer got an initial state in the form of an array ([]). This is just to
// make clear that a reducer can actually handle any type of data structure. It's really
// up to you to decide which data structure suits your needs (an object literal, an array,
// a boolean, a string, an immutable structure, ...).

// With this new multiple reducer approach, we will end up having each reducer handle only
// a slice of our application state.

// But as we already know, createStore expects just one reducer function.

// So how do we combine our reducers? And how do we tell Redux that each reducer will only handle
// a slice of our state?
// It's fairly simple. We use Redux combineReducers helper function. combineReducers takes a hash and
// returns a function that, when invoked, will call all our reducers, retrieve the new slice of state and
// reunite them in a state object (a simple hash {}) that Redux is holding.
// Long story short, here is how you create a Redux instance with multiple reducers:

import { createStore, combineReducers } from 'redux'

var reducer = combineReducers({
    user: userReducer,
    items: itemsReducer
})
// Output:
// userReducer was called with state {} and action { type: '@@redux/INIT' }
// userReducer was called with state {} and action { type: '@@redux/PROBE_UNKNOWN_ACTION_9.r.k.r.i.c.n.m.i' }
// itemsReducer was called with state [] and action { type: '@@redux/INIT' }
// itemsReducer was called with state [] and action { type: '@@redux/PROBE_UNKNOWN_ACTION_4.f.i.z.l.3.7.s.y.v.i' }
var store_0 = createStore(reducer)
// Output:
// userReducer was called with state {} and action { type: '@@redux/INIT' }
// itemsReducer was called with state [] and action { type: '@@redux/INIT' }

// As you can see in the output, each reducer is correctly called with the init action @@redux/INIT.
// But what is this other action? This is a sanity check implemented in combineReducers
// to assure that a reducer will always return a state != 'undefined'.
// Please note also that the first invocation of init actions in combineReducers share the same purpose
// as random actions (to do a sanity check).

console.log('store_0 state after initialization:', store_0.getState())
// Output:
// store_0 state after initialization: { user: {}, items: [] }

// It's interesting to note that Redux handles our slices of state correctly,
// the final state is indeed a simple hash made of the userReducer's slice and the itemsReducer's slice:
// {
//     user: {}, // {} is the slice returned by our userReducer
//     items: [] // [] is the slice returned by our itemsReducer
// }

// Since we initialized the state of each of our reducers with a specific value ({} for userReducer and
// [] for itemsReducer) it's no coincidence that those values are found in the final Redux state.

// By now we have a good idea of how reducers will work. It would be nice to have some
// actions being dispatched and see the impact on our Redux state.

// Go to next tutorial: 06_dispatch-action.js

// Tutorial 06 - dispatch-action.js

// So far we've focused on building our reducer(s) and we haven't dispatched any of our own actions.
// We'll keep the same reducers from our previous tutorial and handle a few actions:

var userReducer = function (state = {}, action) {
    console.log('userReducer was called with state', state, 'and action', action)

    switch (action.type) {
        case 'SET_NAME':
            return {
                ...state,
                name: action.name
            }
        default:
            return state;
    }
}
var itemsReducer = function (state = [], action) {
    console.log('itemsReducer was called with state', state, 'and action', action)

    switch (action.type) {
        case 'ADD_ITEM':
            return [
                ...state,
                action.item
            ]
        default:
            return state;
    }
}

import { createStore, combineReducers } from 'redux'

var reducer = combineReducers({
    user: userReducer,
    items: itemsReducer
})
var store_0 = createStore(reducer)


console.log("\n", '### It starts here')
console.log('store_0 state after initialization:', store_0.getState())
// Output:
// store_0 state after initialization: { user: {}, items: [] }

// Let's dispatch our first action... Remember in 'simple-action-creator.js' we said:
//     "To dispatch an action we need... a dispatch function." Captain obvious

// The dispatch function we're looking for is provided by Redux and will propagate our action
// to all of our reducers! The dispatch function is accessible through the Redux
// instance property "dispatch"

// To dispatch an action, simply call:

store_0.dispatch({
    type: 'AN_ACTION'
})
// Output:
// userReducer was called with state {} and action { type: 'AN_ACTION' }
// itemsReducer was called with state [] and action { type: 'AN_ACTION' }

// Each reducer is effectively called but since none of our reducers care about this action type,
// the state is left unchanged:

console.log('store_0 state after action AN_ACTION:', store_0.getState())
// Output: store_0 state after action AN_ACTION: { user: {}, items: [] }

// But, wait a minute! Aren't we supposed to use an action creator to send an action? We could indeed
// use an actionCreator but since all it does is return an action it would not bring anything more to
// this example. But for the sake of future difficulties let's do it the right way according to
// flux theory. And let's make this action creator send an action we actually care about:

var setNameActionCreator = function (name) {
    return {
        type: 'SET_NAME',
        name: name
    }
}

store_0.dispatch(setNameActionCreator('bob'))
// Output:
// userReducer was called with state {} and action { type: 'SET_NAME', name: 'bob' }
// itemsReducer was called with state [] and action { type: 'SET_NAME', name: 'bob' }

console.log('store_0 state after action SET_NAME:', store_0.getState())
// Output:
// store_0 state after action SET_NAME: { user: { name: 'bob' }, items: [] }

// We just handled our first action and it changed the state of our application!

// But this seems too simple and not close enough to a real use-case. For example,
// what if we'd like do some async work in our action creator before dispatching
// the action? We'll talk about that in the next tutorial "dispatch-async-action.js"

// So far here is the flow of our application
// ActionCreator -> Action -> dispatcher -> reducer

// Go to next tutorial: 07_dispatch-async-action-1.js

// Tutorial 07 - dispatch-async-action-1.js

// We previously saw how we can dispatch actions and how those actions will modify
// the state of our application thanks to reducers.

// But so far we've only considered synchronous actions or, more exactly, action creators
// that produce an action synchronously: when called an action is returned immediately.

// Let's now imagine a simple asynchronous use-case:
// 1) user clicks on button "Say Hi in 2 seconds"
// 2) When button "A" is clicked, we'd like to show message "Hi" after 2 seconds have elapsed
// 3) 2 seconds later, our view is updated with the message "Hi"

// Of course this message is part of our application state so we have to save it
// in Redux store. But what we want is to have our store save the message
// only 2 seconds after the action creator is called (because if we were to update our state
// immediately, any subscriber to state's modifications - like our view -  would be notified right away
// and would then react to this update 2 seconds too soon).

// If we were to call an action creator like we did until now...

import { createStore, combineReducers } from 'redux'

var reducer = combineReducers({
    speaker: function (state = {}, action) {
        console.log('speaker was called with state', state, 'and action', action)

        switch (action.type) {
            case 'SAY':
                return {
                    ...state,
                    message: action.message
                }
            default:
                return state;
        }
    }
})
var store_0 = createStore(reducer)

var sayActionCreator = function (message) {
    return {
        type: 'SAY',
        message
    }
}

console.log("\n", 'Running our normal action creator:', "\n")

console.log(new Date());
store_0.dispatch(sayActionCreator('Hi'))

console.log(new Date());
console.log('store_0 state after action SAY:', store_0.getState())
// Output (skipping initialization output):
//     Sun Aug 02 2015 01:03:05 GMT+0200 (CEST)
//     speaker was called with state {} and action { type: 'SAY', message: 'Hi' }
//     Sun Aug 02 2015 01:03:05 GMT+0200 (CEST)
//     store_0 state after action SAY: { speaker: { message: 'Hi' } }


// ... then we see that our store is updated immediately.

// What we'd like instead is an action creator that looks a bit like this:

var asyncSayActionCreator_0 = function (message) {
    setTimeout(function () {
        return {
            type: 'SAY',
            message
        }
    }, 2000)
}

// But then our action creator would not return an action, it would return "undefined". So this is not
// quite the solution we're looking for.

// Here's the trick: instead of returning an action, we'll return a function. And this function will be the
// one to dispatch the action when it seems appropriate to do so. But if we want our function to be able to
// dispatch the action it should be given the dispatch function. Then, this should look like this:

var asyncSayActionCreator_1 = function (message) {
    return function (dispatch) {
        setTimeout(function () {
            dispatch({
                type: 'SAY',
                message
            })
        }, 2000)
    }
}

// Again you'll notice that our action creator is not returning an action, it is returning a function.
// So there is a high chance that our reducers won't know what to do with it. But you never know, so let's
// try it out and find out what happens...

// Go to next tutorial: 08_dispatch-async-action-2.js

// Tutorial 08 - dispatch-async-action-2.js

// Let's try to run the first async action creator that we wrote in dispatch-async-action-1.js.

import { createStore, combineReducers } from 'redux'

var reducer = combineReducers({
    speaker: function (state = {}, action) {
        console.log('speaker was called with state', state, 'and action', action)

        switch (action.type) {
            case 'SAY':
                return {
                    ...state,
                    message: action.message
                }
            default:
                return state;
        }
    }
})
var store_0 = createStore(reducer)

var asyncSayActionCreator_1 = function (message) {
    return function (dispatch) {
        setTimeout(function () {
            dispatch({
                type: 'SAY',
                message
            })
        }, 2000)
    }
}

console.log("\n", 'Running our async action creator:', "\n")
store_0.dispatch(asyncSayActionCreator_1('Hi'))

// Output:
//     ...
//     /Users/classtar/Codes/redux-tutorial/node_modules/redux/node_modules/invariant/invariant.js:51
//         throw error;
//               ^
//     Error: Invariant Violation: Actions must be plain objects. Use custom middleware for async actions.
//     ...

// It seems that our function didn't even reach our reducers. But Redux has been kind enough to give us a
// tip: "Use custom middleware for async actions.". It looks like we're on the right path but what is this
// "middleware" thing?

// Just to reassure you, our action creator asyncSayActionCreator_1 is well-written and will work as expected
// as soon as we've figured out what middleware is and how to use it.

// Go to next tutorial: 09_middleware.js

// Tutorial 09 - middleware.js

// We left dispatch-async-action-2.js with a new concept: "middleware". Somehow middleware should help us
// to solve async action handling. So what exactly is middleware?

// Generally speaking middleware is something that goes between parts A and B of an application to
// transform what A sends before passing it to B. So instead of having:
// A -----> B
// we end up having
// A ---> middleware 1 ---> middleware 2 ---> middleware 3 --> ... ---> B

// How could middleware help us in the Redux context? Well it seems that the function that we are
// returning from our async action creator cannot be handled natively by Redux but if we had a
// middleware between our action creator and our reducers, we could transform this function into something
// that suits Redux:

// action ---> dispatcher ---> middleware 1 ---> middleware 2 ---> reducers

// Our middleware will be called each time an action (or whatever else, like a function in our
// async action creator case) is dispatched and it should be able to help our action creator
// dispatch the real action when it wants to (or do nothing - this is a totally valid and
// sometimes desired behavior).

// In Redux, middleware are functions that must conform to a very specific signature and follow
// a strict structure:
/*
    var anyMiddleware = function ({ dispatch, getState }) {
        return function(next) {
            return function (action) {
                // your middleware-specific code goes here
            }
        }
    }
*/

// As you can see above, a middleware is made of 3 nested functions (that will get called sequentially):
// 1) The first level provide the dispatch function and a getState function (if your
//     middleware or your action creator needs to read data from state) to the 2 other levels
// 2) The second level provide the next function that will allow you to explicitly hand over
//     your transformed input to the next middleware or to Redux (so that Redux can finally call all reducers).
// 3) the third level provides the action received from the previous middleware or from your dispatch
//     and can either trigger the next middleware (to let the action continue to flow) or process
//     the action in any appropriate way.

// Those of you who are trained to functional programming may have recognized above an opportunity
// to apply a functional pattern: currying (if you aren't, don't worry, skipping the next 10 lines
// won't affect your redux understanding). Using currying, you could simplify the above function like that:
/*
    // "curry" may come any functional programming library (lodash, ramda, etc.)
    var thunkMiddleware = curry(
        ({dispatch, getState}, next, action) => (
            // your middleware-specific code goes here
        )
    );
*/

// The middleware we have to build for our async action creator is called a thunk middleware and
// its code is provided here: https://github.com/gaearon/redux-thunk.
// Here is what it looks like (with function body translated to es5 for readability):

var thunkMiddleware = function ({ dispatch, getState }) {
    // console.log('Enter thunkMiddleware');
    return function(next) {
        // console.log('Function "next" provided:', next);
        return function (action) {
            // console.log('Handling action:', action);
            return typeof action === 'function' ?
                action(dispatch, getState) :
                next(action)
        }
    }
}

// To tell Redux that we have one or more middlewares, we must use one of Redux's
// helper functions: applyMiddleware.

// "applyMiddleware" takes all your middlewares as parameters and returns a function to be called
// with Redux createStore. When this last function is invoked, it will produce "a higher-order
// store that applies middleware to a store's dispatch".
// (from https://github.com/rackt/redux/blob/v1.0.0-rc/src/utils/applyMiddleware.js)

// Here is how you would integrate a middleware to your Redux store:

import { createStore, combineReducers, applyMiddleware } from 'redux'

const finalCreateStore = applyMiddleware(thunkMiddleware)(createStore)
// For multiple middlewares, write: applyMiddleware(middleware1, middleware2, ...)(createStore)

var reducer = combineReducers({
    speaker: function (state = {}, action) {
        console.log('speaker was called with state', state, 'and action', action)

        switch (action.type) {
            case 'SAY':
                return {
                    ...state,
                    message: action.message
                }
            default:
                return state
        }
    }
})

const store_0 = finalCreateStore(reducer)
// Output:
//     speaker was called with state {} and action { type: '@@redux/INIT' }
//     speaker was called with state {} and action { type: '@@redux/PROBE_UNKNOWN_ACTION_s.b.4.z.a.x.a.j.o.r' }
//     speaker was called with state {} and action { type: '@@redux/INIT' }

// Now that we have our middleware-ready store instance, let's try again to dispatch our async action:

var asyncSayActionCreator_1 = function (message) {
    return function (dispatch) {
        setTimeout(function () {
            console.log(new Date(), 'Dispatch action now:')
            dispatch({
                type: 'SAY',
                message
            })
        }, 2000)
    }
}

console.log("\n", new Date(), 'Running our async action creator:', "\n")

store_0.dispatch(asyncSayActionCreator_1('Hi'))
// Output:
//     Mon Aug 03 2015 00:01:20 GMT+0200 (CEST) Running our async action creator:
//     Mon Aug 03 2015 00:01:22 GMT+0200 (CEST) 'Dispatch action now:'
//     speaker was called with state {} and action { type: 'SAY', message: 'Hi' }

// Our action is correctly dispatched 2 seconds after our call the async action creator!

// Just for your curiosity, here is how a middleware to log all actions that are dispatched, would
// look like:

function logMiddleware ({ dispatch, getState }) {
    return function(next) {
        return function (action) {
            console.log('logMiddleware action received:', action)
            return next(action)
        }
    }
}

// Same below for a middleware to discard all actions that goes through (not very useful as is
// but with a bit of more logic it could selectively discard a few actions while passing others
// to next middleware or Redux):
function discardMiddleware ({ dispatch, getState }) {
    return function(next) {
        return function (action) {
            console.log('discardMiddleware action received:', action)
        }
    }
}

// Try to modify finalCreateStore call above by using the logMiddleware and / or the discardMiddleware
// and see what happens...
// For example, using:
//     const finalCreateStore = applyMiddleware(discardMiddleware, thunkMiddleware)(createStore)
// should make your actions never reach your thunkMiddleware and even less your reducers.

// See http://rackt.org/redux/docs/introduction/Ecosystem.html, section Middlewares, to
// see other middleware examples.

// Let's sum up what we've learned so far:
// 1) We know how to write actions and action creators
// 2) We know how to dispatch our actions
// 3) We know how to handle custom actions like asynchronous actions thanks to middlewares

// The only missing piece to close the loop of Flux application is to be notified about
// state updates to be able to react to them (by re-rendering our components for example).

// So how do we subscribe to our Redux store updates?

// Go to next tutorial: 10_state-subscriber.js

// Tutorial 10 - state-subscriber.js

// We're close to having a complete Flux loop but we still miss one critical part:

//  _________      _________       ___________
// |         |    | Change  |     |   React   |
// |  Store  |----▶ events  |-----▶   Views   |
// |_________|    |_________|     |___________|

// Without it, we cannot update our views when the store changes.

// Fortunately, there is a very simple way to "watch" over our Redux's store updates:

/*
    store.subscribe(function() {
        // retrieve latest store state here
        // Ex:
        console.log(store.getState());
    })
*/

// Yeah... So simple that it almost makes us believe in Santa Claus again.

// Let's try this out:

import { createStore, combineReducers } from 'redux'

var itemsReducer = function (state = [], action) {
    console.log('itemsReducer was called with state', state, 'and action', action)

    switch (action.type) {
        case 'ADD_ITEM':
            return [
                ...state,
                action.item
            ]
        default:
            return state;
    }
}

var reducer = combineReducers({ items: itemsReducer })
var store_0 = createStore(reducer)

store_0.subscribe(function() {
    console.log('store_0 has been updated. Latest store state:', store_0.getState());
    // Update your views here
})

var addItemActionCreator = function (item) {
    return {
        type: 'ADD_ITEM',
        item: item
    }
}

store_0.dispatch(addItemActionCreator({ id: 1234, description: 'anything' }))

// Output:
//     ...
//     store_0 has been updated. Latest store state: { items: [ { id: 1234, description: 'anything' } ] }

// Our subscribe callback is correctly called and our store now contains the new item that we added.

// Theoretically speaking we could stop here. Our Flux loop is closed, we understood all concepts that make
// Flux and we saw that it is not that much of a mystery. But to be honest, there is still a lot to talk
// about and a few things in the last example were intentionally left aside to keep the simplest form of this
// last Flux concept:

// - Our subscriber callback did not receive the state as a parameter, why?
// - Since we did not receive our new state, we were bound to exploit our closured store (store_0) so this
//     solution is not acceptable in a real multi-modules application...
// - How do we actually update our views?
// - How do we unsubscribe from store updates?
// - More generally speaking, how should we integrate Redux with React?

// We're now entering a more "Redux inside React" specific domain.

// It is very important to understand that Redux is by no means bound to React. It is really a
// "Predictable state container for JavaScript apps" and you can use it in many ways, a React
// application just being one of them.

// In that perspective we would be a bit lost if it wasn't for react-redux (https://github.com/rackt/react-redux).
// Previously integrated inside Redux (before 1.0.0), this repository holds all the bindings we need to simplify
// our life when using Redux inside React.

// Back to our "subscribe" case... Why exactly do we have this subscribe function that seems so simple but at
// the same time also seems to not provide enough features?

// Its simplicity is actually its power! Redux, with its current minimalist API (including "subscribe") is
//  highly extensible and this allows developers to build some crazy products like the Redux DevTools
// (https://github.com/gaearon/redux-devtools).

// But in the end we still need a "better" API to subscribe to our store changes. That's exactly what react-redux
// brings us: an API that will allow us to seamlessly fill the gap between the raw Redux subscribing mechanism
// and our developer expectations. In the end, you won't need to use "subscribe" directly. Instead you will
// use bindings such as "provide" or "connect" and those will hide from you the "subscribe" method.

// So yeah, the "subscribe" method will still be used but it will be done through a higher order API that
// handles access to redux state for you.

// We'll now cover those bindings and show how simple it is to wire your components to Redux's state.

// Go to next tutorial: 11_Provider-and-connect.js

// Tutorial 11 - Provider-and-connect.js

// This is the final tutorial and the one that will show you how to bind together Redux and React.

// To run this example, you will need a browser.

// All explanations for this example are inlined in the sources inside ./11_src/src/.

// Once you've read the lines below, start with 11_src/src/server.js.

// To build our React application and serve it to a browser, we'll use:
// - A very simple node HTTP server (https://nodejs.org/api/http.html)
// - The awesome Webpack (http://webpack.github.io/) to bundle our application,
// - The magic Webpack Dev Server (http://webpack.github.io/docs/webpack-dev-server.html)
//     to serve JS files from a dedicated node server that allows for files watch
// - The incredible React Hot Loader http://gaearon.github.io/react-hot-loader/ (another awesome
//     project of Dan Abramov - just in case, he is Redux's author) to have a crazy
//     DX (Developer experience) by having our components live-reload in the browser
//     while we're tweaking them in our code editor.

// An important point for those of you who are already using React: this application is built
// upon React 0.14.

// I won't detail Webpack Dev Server and React Hot Loader setup here since it's done pretty
// well in React Hot Loader's docs.
import webpackDevServer from './11_src/src/webpack-dev-server'
// We request the main server of our app to start it from this file.
import server from './11_src/src/server'

// Change the port below if port 5050 is already in use for you.
// if port equals X, we'll use X for server's port and X+1 for webpack-dev-server's port
const port = 5050

// Start our webpack dev server...
webpackDevServer.listen(port)
// ... and our main app server.
server.listen(port)

console.log(`Server is listening on http://127.0.0.1:${port}`)

// Go to 11_src/src/server.js...

// Go to next tutorial: 12_final-words.js

Code

package.json

{
  "name": "redux-tutorial",
  "version": "0.0.1",
  "description": "Learn how to use redux step by step",
  "repository": {
    "type": "git",
    "url": "https://github.com/happypoulp/redux-tutorial.git"
  },
  "scripts": {
    "example": "babel-node"
  },
  "author": "François Bonnefont",
  "license": "ISC",
  "dependencies": {
    "babel": "5.3.1",
    "babel-core": "^5.6.15",
    "babel-loader": "^5.2.2",
    "bluebird": "^2.9.34",
    "css-loader": "^0.15.1",
    "file-loader": "^0.8.4",
    "node-libs-browser": "^0.5.2",
    "react": "0.14.2",
    "react-dom": "0.14.2",
    "react-redux": "4.0.0",
    "redux": "v3.0.2",
    "redux-thunk": "^0.1.0",
    "style-loader": "^0.12.3",
    "url-loader": "^0.5.6",
    "webpack": "^1.10.0"
  },
  "devDependencies": {
    "react-hot-loader": "^1.2.7",
    "eslint": "^1.10.3",
    "babel-eslint": "^3.1.9",
    "eslint-plugin-react": "^2.3.0",
    "webpack-dev-server": "^1.8.2"
  }
}

webpack.config.js

var path = require('path')
var webpack = require('webpack')

var webpackDevHost = 'localhost'

module.exports = function (webpackDevPort) {
  return {
    devtool: 'eval',
    entry: [
      'webpack-dev-server/client?http://' + webpackDevHost + ':' + webpackDevPort,
      'webpack/hot/only-dev-server',
      './11_src/src/index'
    ],
    output: {
      path: path.join(__dirname, 'dist'),
      filename: 'bundle.js',
      publicPath: 'http://' + webpackDevHost + ':' + webpackDevPort + '/static/'
    },
    plugins: [
      new webpack.HotModuleReplacementPlugin(),
      new webpack.NoErrorsPlugin()
    ],
    resolve: {
      extensions: ['', '.js', '.jsx']
    },
    module: {
      loaders: [{
        test: /\.jsx?$/,
        loaders: ['react-hot', 'babel'],
        include: path.join(__dirname, 'src')
      }]
    }
  }
}

src

webpack-dev-server.js

var webpack = require('webpack')
var WebpackDevServer = require('webpack-dev-server')
var getConfig = require('../webpack.config')

const webpackDevHost = 'localhost'

export default {
  listen: (serverPort) => {
    const webpackDevPort = serverPort + 1
    const config = getConfig(webpackDevPort);

    const webpackDevServer = new WebpackDevServer(
      webpack(config),
      {
        publicPath: config.output.publicPath,
        hot: true,
        historyApiFallback: true
      }
    )

    webpackDevServer.listen(
      webpackDevPort,
      webpackDevHost,
      function (err, result) {
        if (err) {
        console.log(err)
        }

        console.log('Listening at ' + webpackDevHost + ':' + webpackDevPort)
      }
    )
  }
}

server.js

// Tutorial 12 - Provider-and-connect.js

// Hi there! So you're ready to play with Redux in a React app?

// The application you're about to see could not be more minimalist (from a product point of view
// and from a design point of view - sorry for the black and white)... We'll only focus
// on the use of 2 main bindings of react-redux (https://github.com/gaearon/react-redux):
// 1) the Provider component
// 2) the connect function

// But before we get to that, let's see the basic setup of this application and how it
// will be served to browser...

// We won't use Express (http://expressjs.com/) in this app since we don't really need
// it to serve a simple html page.

// "http" module will be used to create the http server
import http from 'http'
import React from 'react'

// We create our main application server here. It will serve the same page on all URIs
// so you won't find any route specific logic below (except for rejecting favicon request)
var server = http.createServer(function(req, res) {

  // Forget this, it's just to avoid serving anything when the browser automatically
  // requests favicon (if not, this server would send back an html page).
  if (req.url.match('favicon.ico')) {
    return res.end()
  }

  // And of course, here is our Application HTML that we're sending back to the browser.
  // Nothing special here except the URI of our application JS bundle that points to our
  // webpack dev server (located at http://localhost:5051)
  res.write(
    `<!DOCTYPE html>
    <html>
      <head>
        <meta charSet="utf-8" />
      </head>
      <body>
        <div id="app-wrapper"></div>
        <script type="text/javascript" src="http://localhost:5051/static/bundle.js"></script>
      </body>
    </html>`
  )

  res.end()
})

export default server

// Go to ./index.jsx, where our app is initialized. For those of you who are not familiar with webpack,
// index.jsx is defined as the entry point (the first file) of our JS bundle (in 12_src/webpack.config.js)
// and is automatically executed when the JS bundle is loaded in our browser.

index.jsx

// Tutorial 12 - Provider-and-connect.js

// This file is the entry point of our JS bundle. It's here that we'll create our Redux store,
// instantiate our React Application root component and attach it to the DOM.

import React from 'react'
import { render } from 'react-dom'
// All store creation specific code is located in ./create-store.js
import createStore from './create-store'
// Application is the root component of our application and the one that holds Redux's Provider...
import Application from './application'

// Just as we did so many times in previous examples, we need to create our redux instance. This time
// all code for that task was moved to a specific module that returns a single function to trigger the
// instantiation.
const store = createStore()

// Now, time to render our application to the DOM using ReactDOM.render (or just render thanks to
// the ES6 notation: import { render } from 'react-dom')...
render(
  // ... and to provide our Redux store to our Root component as a prop so that Redux
  // Provider can do its job.
  <Application store={store} />,
  document.getElementById('app-wrapper')
)

// Go to ./create-store.js to review what you know now perfectly: "How to create a Redux store?"

create-store.js

// Tutorial 12 - Provider-and-connect.js

// There is not much to say here, you've seen this plenty of times and it should feel pretty
// familiar to you now...

// One thing to notice though: we're not using the thunk middleware that we've seen before. Instead
// we use a promise middleware solution that will allow us to handle asynchronous action creators and
// to do some nice real time updates on our UI (could also do some optimistic updates).
// This middleware was discussed here: https://github.com/rackt/redux/issues/99 and it is used
// in this very good react-redux-universal-example: https://github.com/erikras/react-redux-universal-hot-example
// that I strongly suggest you get a look at (later, not right now ;)).

import { createStore, applyMiddleware, combineReducers } from 'redux'
// You can go and see the code for this middleware, it's not very complicated and makes a good
// exercise to sharpen your understanding on middlewares.
import promiseMiddleware from './promise-middleware'
// We'll just have one reducer in this application but the ES6 import notation below is
// pretty interesting to import and produce a reducers hash in one go. Have a look in
// ./reducers.js to see what our reducer actually do (no magic there).
import * as reducers from './reducers'

// The data parameter that we see here is used to initialize our redux store with data. We didn't
// talk about this yet for simplicity but thanks to it your reducers can be initialized
// with real data if you already have some. For example in an isomorphic/universal app where you
// fetch data server-side, serialize and pass it to the client, your Redux store can be
// initialized with that data.
// We're not passing any data here but it's good to know about this createStore's ability.
export default function(data) {
  var reducer = combineReducers(reducers)
  var finalCreateStore = applyMiddleware(promiseMiddleware)(createStore)
  var store = finalCreateStore(reducer, data)

  return store
}

// Go to ./application.jsx to learn of the first Redux binding for React: the Provider component.

promise-middleware.js

export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, types, ...rest } = action

    if (!promise) {
      return next(action)
    }

    const [REQUEST, SUCCESS, FAILURE] = types

    next({...rest, type: REQUEST})

    return promise().then(
      (result) => {
        next({...rest, result, type: SUCCESS})
      },
      (error) => {
        next({...rest, error, type: FAILURE})
      }
    )
  }
}

reducers.js

// Tutorial 12 - Provider-and-connect.js

// This file holds the one and only reducer of our application. Its behavior is nothing new to you
// except maybe its handling of three aspects of an action (GET_TIME) that become 3 dedicated actions...
// This approach allows us to do some nice real time updates in our UI like this:
// 1) When we receive GET_TIME_REQUEST action, we modify the state to say that some part of the
//    UI should be frozen (because there is a pending operation)
// 2) When we receive GET_TIME_SUCCESS (or GET_TIME_FAILURE) later on, we modify the state to
//    unfreeze our application and to add the new data we received.

var initialTimeState = {}

// The reducer is named with leading "_" to avoid having: state.time.time (time twice) when reading
// from state. So it's just a personal preference here and you may not need this depending on
// how your reducers are named and what properties they expose in Redux's store.
export function _time(state = initialTimeState, action) {
  console.log('_time reducer called with state ', state , ' and action ', action);

  switch (action.type) {
    case 'GET_TIME_REQUEST':
      return {
        ...state,
        frozen: true
      }
    case 'GET_TIME_SUCCESS':
      return {
        ...state,
        time: action.result.time,
        frozen: false
      }
    case 'GET_TIME_FAILURE':
      // we could add an error message here, to be printed somewhere in our application
      return {
        ...state,
        frozen: false
      }
    default:
      return state
  }
}

application.jsx

// Tutorial 12 - Provider-and-connect.js

// Now is the time to meet the first binding that redux-react (https://github.com/rackt/react-redux)
// brings to us: the Provider component.

// Provider is a React Component designed to be used as a wrapper of your application's root component. Its
// purpose is to provide your redux instance to all of your application's components. How it does that does not
// really matter to us but just to let you know, it's using React's context feature (it's undocumented so you
// don't have to know about it, but if you're curious:
// https://www.tildedave.com/2014/11/15/introduction-to-contexts-in-react-js.html).

import React from 'react'
import Home from './home'
import { Provider } from 'react-redux'

export default class Application extends React.Component {
  render () {
    return (
      // As explained above, the Provider must wrap your application's Root component. This way,
      // this component and all of its children (even deeply nested ones) will have access to your
      // Redux store. Of course, to allow Provider to do that, you must give it the store
      // you built previously (via a "store" props).
      <Provider store={ this.props.store }>
        <Home />
      </Provider>
    )
  }
}

// Go to ./home.jsx to discover how you could read from state and dispatch an action from a React component.

home.jsx

// Tutorial 12 - Provider-and-connect.js

// Our tutorial is almost over and the only missing piece to leave you with a good overview of Redux is:
// How do we read from our store's state and how do we dispatch actions?

// Both of these questions can be answered using a single react-redux's binding: connect.

// As we previously explained, when using the Provider component we allow all components of our app to
// access Redux. But this access can only be made through the undocumented feature "React's context". To
// avoid asking you to use such a "dark" React API, React-Redux is exposing a function that you can use
// on a component class.

// The function we're talking about is "connect" and it allows to literally connect your component with your Redux's store.
// By doing so, it provides your store's dispatch function through a component's prop and also adds any
// properties you want to expose as part of your store's state.

// Using "connect", you'll turn a dumb component into a smart component with very little code overhead
// (https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0).

// "connect" is a function that takes as parameters few mapping functions and that returns a function expecting
// the actual component class you want to connect. Such a function (connect) is called a Higher Order Component (HOC).
// Higher Order functions comes from a functional pattern designed to add features / behaviors to
// their inputs (component, store, ...) without using inheritance. This approach favors composition over inheritance
// which is the prefered way to build React applications (actually this is not limited at all to React applications).
// Read more about HOCs and composition here:
// - https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750#.lpp7we7mx
// - http://natpryce.com/articles/000814.html

// The "connect" HOC is designed to address all use-cases, from the most simple to the most
// complex ones. In the present example, we're not going to use the most complex form of 'connect' but
// you can find all information about it in the complete 'connect' API documentation here:
// https://github.com/rackt/react-redux/blob/v4.0.0/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options

// Here is the complete 'connect' signature:
// connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
// and here is how you're supposed to use it:
/*
  const wrappedComponentClass = connect(...)(ComponentClass)
*/

// We will only focus here on the first 'connect' parameter: mapStateToProps...

// "connect" takes, as its first parameter, a function that will select which slice of your
// state you want to expose to your component. This function is logically called a "selector" and
// receives 2 parameters: the state of your store and the current props of your component.
// You'll see below that we named this function "mapStateToProps". This name is just a semantic name
// for our function that clearly expresses what the function does: it maps (read "extracts some of")
//  the state to a few component props.
// The props of the component are also provided as arguments to handle common cases like extracting a slice of your
// state depending on a prop value (Ex: state.items[props.someID]).
// "mapStateToProps" is expected to return the props that you wish to expose to your component (usually via
// an object literal). It's up to you to eventually transform the state you're receiving before returning it.
// You can have a look right at that simplest 'connect' usage below (just after the component class definition).

import React from 'react'
import { connect } from 'react-redux'
// We use the same ES6 import trick to get all action creators and produce a hash like we did with
// our reducers. If you haven't yet, go get a look at our action creator (./action-creators.js).
import * as actionCreators from './action-creators'

class Home extends React.Component {
  onTimeButtonClick (delay) {
    // This button handler will dispatch an action in response to a click event from a user.
    // We use here the dispatch function "automatically" provided by connect in a prop.
    // There are alternative ways to call actionCreators that are already bound to dispatch and those
    // imply providing the second parameter to 'connect':
    // https://github.com/rackt/react-redux/blob/v4.0.0/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options
    // The "delay" value given to actionCreators.getTime is a delay to simulate an async work being done before we
    // are able to get the current time. Try to change this value to verify that the delay correctly impacts our UI.
    this.props.dispatch(actionCreators.getTime(delay))
  }
  render () {

    // Thanks to "connect", we're able to get specific selected data, through the props.
    var { frozen, time, reduxState } = this.props
    var attrs = {}
    const DELAY = 500 // in ms

    if (frozen) {
        attrs = {
          disabled: true
        }
    }

    return (
      <div>
        <h1>Provider and connect example</h1>
        <span>
          <b>What time is it?</b> { time ? `It is currently ${time}` : 'No idea yet...' }
        </span>
        <br /> <br />
        <i>
          When clicking the button below, the time will be provided after a {DELAY}ms delay.<br />
          Try to change this value (in <b>src/home.jsx - line 77</b>) to verify that the delay given correctly impacts our UI.
        </i>
        <br />
        {/* We register our button "onClick" handler here: */}
        <button { ...attrs } onClick={() => this.onTimeButtonClick(DELAY)}>Get time!</button>
        <pre>
          redux state = { JSON.stringify(reduxState, null, 2) }
        </pre>
      </div>
    )
  }
}

// This is our select function that will extract from the state the data slice we want to expose
// through props to our component.
const mapStateToProps = (state/*, props*/) => {
  return {
    frozen: state._time.frozen,
    time: state._time.time,
    // It is very bad practice to provide the full state like that (reduxState: state) and it is only done here
    // for you to see its stringified version in our page. More about that here:
    // https://github.com/rackt/react-redux/blob/v4.0.0/docs/api.md#inject-dispatch-and-every-field-in-the-global-state
    reduxState: state,
  }
}

const ConnectedHome = connect(mapStateToProps)(Home)

export default ConnectedHome

// You might have noticed that thanks to redux, while we have a dynamic component that requires some state (to keep
// the current time), this state is by no mean present inside the component. Our component only receives props with
// needed data.
// What we have here is called a stateless component. You should always try to have more stateless components (presented
// above as dumb components) in your applications than stateful ones because they are much more reusable.
// As suggested in "onTimeButtonClick" handler, we could even go further by passing our click callback as a prop
// via "connect" second parameter "mapDispatchToProps". Doing so, we would extract our button behavior outside of
// our component, making it even more reusable by allowing for a different click behavior.
// Reusability might seem like a fancy overused concept but what having a reusable component also means, is that it's
// one component that can be very easily tested (because you can then inject in your component whatever data and
// test handlers you want and easily ensure its correct behavior).

// Before going to ./12_final-words.js, read this side-note about an alternative way to use "connect" HOC...

// Because connect(...) returns a function that accept a class and returns another class, you can use it as
// an ES7 decorator if you want to. Decorators are an experimental ES7 features that make it possible to annotate
// and modify classes and properties at design time (https://github.com/wycats/javascript-decorators).

// This feature being experimental, it is subject to change and breakage. This means that by using it today, you must be
// fully aware of and accept the uncertainty regarding its evolution. Decorators provide syntax sugar to write the
// code above slightly differently. Instead of writing:

/*
  class MyClass {}
  export default somedecorator(MyClass)
*/

// You can write:

/*
  @somedecorator
  export default class MyClass {}
*/

// Applying this syntax to redux connect, you can replace:

/*
  let mapStateToProps = (state) => { ... }
  class MyClass {}
  export default connect(mapStateToProps)(MyClass)
*/

// by:

/*
  let mapStateToProps = (state) => { ... }
  @connect(mapStateToProps)
  export default class MyClass {}
*/

// As you can see the application of the HOC function on the component class is now made implicit ( @connect(mapStateToProps) )
// instead of calling it ourselves ( @connect(mapStateToProps)(Myclass) ). Some find this approach more elegant, others
// dislike the fact that it's hiding what is really happening and many just don't get how decorators works. Knowing all that
// and remembering that decorators are still experimental, you can now decide by youselves which "connect" usage you
// prefer and you won't be suprised to find both syntax in the many articles, tutorials, starter kits, etc. out there.

// Go to ./12_final-words.js for our last advice about what to do now...

action-creators.js

// Tutorial 12 - Provider-and-connect.js

// We're using Bluebird (https://github.com/petkaantonov/bluebird) as promise library but you could really
// use any promise lib you want.
import Promise from 'bluebird'

// Our action creator just gets the current time in a delayed fashion to illustrate the use of the promise
// middleware.

// The promise middleware works by waiting for either:
// 1) an action with this format:
//    {
//      types: [REQUEST, SUCCESS, FAILURE], // actions types given in this specific order
//      promise: function() {
//        // return a promise
//      }
//    }
// 2) or anything else what would be passed to the next middleware or to Redux (actually, with this
//    implementation of the promise middleware, the "anything else" has to NOT contain a promise
//    property to be passed to the next middleware or Redux)

// When the promise middleware receives this action, it will create 2 actions from this one:
// 1 action for the REQUEST and later 1 action for the SUCCESS or the FAILURE of the action creator.

// Again, the code for the promise middleware is not complicated and it is worth having a look
// at it (./promise-middleware.js)

// The action is delayed by "delay" ms passed as a parameter of the action creator. Try to change
// this value to verify that the delay correctly impacts our UI.
export function getTime(delay) {
  return {
    types: ['GET_TIME_REQUEST', 'GET_TIME_SUCCESS', 'GET_TIME_FAILURE'],
    promise: () => {
      return new Promise((resolve, reject) => {
        // Just simulating an async request to a server via a setTimeout
        setTimeout(() => {
          const d = new Date()
          const ms = ('000' + d.getMilliseconds()).slice(-3)
          resolve({
            time: `${d.toString().match(/\d{2}:\d{2}:\d{2}/)[0]}.${ms}`
          })
        }, delay)
      })
    }
  }
}

// Tutorial 12 - final-words.js

// There is actually more to Redux and react-redux than what we showed you with this tutorial. For example,
// concerning Redux, you may be interested in bindActionCreators (to produce a hash of action creators
// already bound to dispatch - http://rackt.org/redux/docs/api/bindActionCreators.html).

// We hope we've given you the keys to better understand Flux and to see more clearly
// how Flux implementations differ from one another--especially how Redux stands out ;).

// Where to go from here?

// The official Redux documentation is really awesome and exhaustive so you should not hesitate to
// refer to it from now on: http://rackt.org/redux/index.html

// Have fun with React and Redux!