├── chapter-03 ├── flux │ ├── config │ │ ├── jest │ │ │ ├── CSSStub.js │ │ │ └── FileStub.js │ │ ├── polyfills.js │ │ └── env.js │ ├── src │ │ ├── index.css │ │ ├── AppDispatcher.js │ │ ├── ActionTypes.js │ │ ├── index.js │ │ ├── Actions.js │ │ ├── views │ │ │ ├── ControlPanel.js │ │ │ └── Summary.js │ │ └── stores │ │ │ └── CounterStore.js │ ├── public │ │ └── favicon.ico │ ├── .gitignore │ └── scripts │ │ └── test.js ├── react-redux │ ├── config │ │ ├── jest │ │ │ ├── CSSStub.js │ │ │ └── FileStub.js │ │ └── polyfills.js │ ├── src │ │ ├── index.css │ │ ├── ActionTypes.js │ │ ├── Store.js │ │ ├── Actions.js │ │ ├── index.js │ │ ├── Reducer.js │ │ └── views │ │ │ ├── ControlPanel.js │ │ │ └── Summary.js │ ├── public │ │ └── favicon.ico │ └── scripts │ │ └── test.js ├── redux_basic │ ├── config │ │ ├── jest │ │ │ ├── CSSStub.js │ │ │ └── FileStub.js │ │ └── polyfills.js │ ├── src │ │ ├── index.css │ │ ├── ActionTypes.js │ │ ├── index.js │ │ ├── Store.js │ │ ├── Actions.js │ │ ├── Reducer.js │ │ └── views │ │ │ ├── ControlPanel.js │ │ │ └── Summary.js │ ├── public │ │ └── favicon.ico │ └── scripts │ │ └── test.js ├── redux_smart_dumb │ ├── config │ │ ├── jest │ │ │ ├── CSSStub.js │ │ │ └── FileStub.js │ │ └── polyfills.js │ ├── src │ │ ├── index.css │ │ ├── ActionTypes.js │ │ ├── index.js │ │ ├── Store.js │ │ ├── Actions.js │ │ ├── Reducer.js │ │ └── views │ │ │ └── ControlPanel.js │ ├── public │ │ └── favicon.ico │ └── scripts │ │ └── test.js └── redux_with_context │ ├── config │ ├── jest │ │ ├── CSSStub.js │ │ └── FileStub.js │ └── polyfills.js │ ├── src │ ├── index.css │ ├── ActionTypes.js │ ├── Store.js │ ├── index.js │ ├── Actions.js │ ├── Reducer.js │ ├── Provider.js │ └── views │ │ └── ControlPanel.js │ ├── public │ └── favicon.ico │ └── scripts │ └── test.js ├── chapter-02 ├── controlpanel │ ├── config │ │ ├── jest │ │ │ ├── CSSStub.js │ │ │ └── FileStub.js │ │ └── polyfills.js │ ├── src │ │ ├── index.css │ │ ├── index.js │ │ └── ControlPanel.js │ ├── public │ │ └── favicon.ico │ ├── .gitignore │ └── scripts │ │ └── test.js └── controlpanel_with_summary │ ├── config │ ├── jest │ │ ├── CSSStub.js │ │ └── FileStub.js │ └── polyfills.js │ ├── src │ ├── index.css │ ├── index.js │ └── ControlPanel.js │ ├── public │ └── favicon.ico │ ├── .gitignore │ └── scripts │ └── test.js ├── chapter-01 ├── first_react_app │ ├── config │ │ ├── jest │ │ │ ├── CSSStub.js │ │ │ └── FileStub.js │ │ └── polyfills.js │ ├── src │ │ ├── index.css │ │ ├── App.test.js │ │ ├── index.js │ │ ├── App.css │ │ ├── App.js │ │ └── ClickCounter.js │ ├── public │ │ └── favicon.ico │ ├── .gitignore │ └── scripts │ │ └── test.js └── jquery_solution │ ├── clickCounter.js │ └── index.html ├── chapter-04 ├── todo │ ├── src │ │ ├── filter │ │ │ ├── actionTypes.js │ │ │ ├── actions.js │ │ │ ├── index.js │ │ │ ├── reducer.js │ │ │ └── views │ │ │ │ ├── filters.js │ │ │ │ ├── style.css │ │ │ │ └── link.js │ │ ├── constants.js │ │ ├── todos │ │ │ ├── actionTypes.js │ │ │ ├── index.js │ │ │ ├── views │ │ │ │ ├── todos.js │ │ │ │ └── todoItem.js │ │ │ ├── actions.js │ │ │ └── reducer.js │ │ ├── TodoApp.js │ │ ├── index.js │ │ └── Store.js │ ├── public │ │ └── favicon.ico │ ├── .gitignore │ └── package.json └── todo_controlled_component │ ├── src │ ├── filter │ │ ├── actionTypes.js │ │ ├── actions.js │ │ ├── index.js │ │ ├── reducer.js │ │ └── views │ │ │ ├── filters.js │ │ │ ├── style.css │ │ │ └── link.js │ ├── constants.js │ ├── todos │ │ ├── actionTypes.js │ │ ├── index.js │ │ ├── views │ │ │ ├── todos.js │ │ │ └── todoItem.js │ │ ├── actions.js │ │ └── reducer.js │ ├── TodoApp.js │ ├── index.js │ └── Store.js │ ├── public │ └── favicon.ico │ └── package.json ├── chapter-05 ├── todo_perf │ ├── src │ │ ├── filter │ │ │ ├── actionTypes.js │ │ │ ├── actions.js │ │ │ ├── index.js │ │ │ ├── reducer.js │ │ │ └── views │ │ │ │ ├── filters.js │ │ │ │ ├── style.css │ │ │ │ └── link.js │ │ ├── constants.js │ │ ├── todos │ │ │ ├── actionTypes.js │ │ │ ├── index.js │ │ │ ├── views │ │ │ │ └── todos.js │ │ │ ├── actions.js │ │ │ └── reducer.js │ │ ├── TodoApp.js │ │ ├── index.js │ │ └── Store.js │ ├── public │ │ └── favicon.ico │ └── package.json ├── todo_with_selector │ ├── src │ │ ├── filter │ │ │ ├── actionTypes.js │ │ │ ├── actions.js │ │ │ ├── index.js │ │ │ ├── reducer.js │ │ │ └── views │ │ │ │ ├── filters.js │ │ │ │ ├── style.css │ │ │ │ └── link.js │ │ ├── constants.js │ │ ├── todos │ │ │ ├── actionTypes.js │ │ │ ├── index.js │ │ │ ├── views │ │ │ │ ├── todos.js │ │ │ │ └── todoList.js │ │ │ ├── actions.js │ │ │ ├── selector.js │ │ │ └── reducer.js │ │ ├── TodoApp.js │ │ ├── index.js │ │ └── Store.js │ ├── public │ │ └── favicon.ico │ ├── package.json │ └── test │ │ ├── filter │ │ └── views │ │ │ └── filters.test.js │ │ └── todos │ │ ├── actions.test.js │ │ └── views │ │ └── todoList.test.js └── .gitignore ├── chapter-10 ├── todo_animated │ ├── src │ │ ├── filter │ │ │ ├── actionTypes.js │ │ │ ├── actions.js │ │ │ ├── index.js │ │ │ ├── reducer.js │ │ │ └── views │ │ │ │ ├── filters.js │ │ │ │ ├── style.css │ │ │ │ └── link.js │ │ ├── constants.js │ │ ├── todos │ │ │ ├── actionTypes.js │ │ │ ├── index.js │ │ │ ├── views │ │ │ │ ├── todos.js │ │ │ │ ├── todoItem.css │ │ │ │ └── todoList.js │ │ │ ├── actions.js │ │ │ ├── selector.js │ │ │ └── reducer.js │ │ ├── TodoApp.js │ │ ├── index.js │ │ └── Store.js │ ├── public │ │ └── favicon.ico │ └── package.json ├── todo_react_motion │ ├── src │ │ ├── filter │ │ │ ├── actionTypes.js │ │ │ ├── actions.js │ │ │ ├── index.js │ │ │ ├── reducer.js │ │ │ └── views │ │ │ │ ├── filters.js │ │ │ │ ├── style.css │ │ │ │ └── link.js │ │ ├── constants.js │ │ ├── todos │ │ │ ├── actionTypes.js │ │ │ ├── index.js │ │ │ ├── views │ │ │ │ └── todos.js │ │ │ ├── actions.js │ │ │ ├── selector.js │ │ │ └── reducer.js │ │ ├── TodoApp.js │ │ ├── index.js │ │ └── Store.js │ ├── public │ │ └── favicon.ico │ └── package.json └── animation_types │ ├── css3_sample.html │ ├── setInterval_animation.html │ └── simulate_requestAnimationFrame.html ├── chapter-07 ├── weather_redux │ ├── src │ │ ├── city_selector │ │ │ └── index.js │ │ ├── weather │ │ │ ├── status.js │ │ │ ├── index.js │ │ │ ├── actionTypes.js │ │ │ ├── reducer.js │ │ │ └── actions.js │ │ ├── index.css │ │ ├── index.js │ │ ├── App.js │ │ └── Store.js │ ├── public │ │ └── favicon.ico │ ├── .gitignore │ ├── test │ │ └── weather │ │ │ └── reducer.test.js │ └── package.json ├── weather_redux_improved │ ├── src │ │ ├── city_selector │ │ │ └── index.js │ │ ├── weather │ │ │ ├── status.js │ │ │ ├── index.js │ │ │ ├── actionTypes.js │ │ │ └── reducer.js │ │ ├── index.css │ │ ├── index.js │ │ ├── App.js │ │ └── Store.js │ ├── public │ │ └── favicon.ico │ ├── .gitignore │ └── package.json └── weather_react │ ├── src │ ├── index.css │ └── index.js │ ├── public │ └── favicon.ico │ ├── .gitignore │ └── package.json ├── chapter-09 ├── weather_with_promise_middleware │ ├── src │ │ ├── city_selector │ │ │ └── index.js │ │ ├── weather │ │ │ ├── status.js │ │ │ ├── index.js │ │ │ ├── actionTypes.js │ │ │ ├── actions.js │ │ │ └── reducer.js │ │ ├── index.css │ │ ├── index.js │ │ ├── App.js │ │ ├── middleware │ │ │ └── promise_middleware.js │ │ └── Store.js │ ├── public │ │ └── favicon.ico │ └── package.json ├── reset_enhancer │ ├── package.json │ ├── src │ │ └── reset.js │ └── test │ │ └── reset.test.js └── promise_middleware │ ├── package.json │ └── src │ ├── simple.js │ └── advanced.js ├── chapter-12 ├── isomorphic │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── index.js │ │ ├── components │ │ │ ├── Counter │ │ │ │ ├── actionTypes.js │ │ │ │ ├── index.js │ │ │ │ ├── actions.js │ │ │ │ ├── reducer.js │ │ │ │ └── view.js │ │ │ └── TopMenu │ │ │ │ └── index.js │ │ ├── pages │ │ │ ├── About.js │ │ │ ├── NotFound.js │ │ │ ├── App.js │ │ │ ├── Home.js │ │ │ └── CounterPage.js │ │ ├── enhancer │ │ │ └── reset.js │ │ └── Store.js │ ├── config │ │ ├── jest │ │ │ ├── fileTransform.js │ │ │ └── cssTransform.js │ │ └── polyfills.js │ ├── server │ │ ├── index.js │ │ ├── views │ │ │ └── index.ejs │ │ └── app.prod.js │ └── scripts │ │ └── test.js └── express_server │ ├── public │ └── favicon.ico │ ├── src │ ├── components │ │ ├── Counter │ │ │ ├── actionTypes.js │ │ │ ├── index.js │ │ │ ├── actions.js │ │ │ ├── reducer.js │ │ │ └── view.js │ │ └── TopMenu │ │ │ └── index.js │ ├── pages │ │ ├── About.js │ │ ├── NotFound.js │ │ ├── App.js │ │ ├── Home.js │ │ └── CounterPage.js │ ├── index.js │ ├── enhancer │ │ └── reset.js │ └── Store.js │ ├── config │ ├── jest │ │ ├── fileTransform.js │ │ └── cssTransform.js │ └── polyfills.js │ ├── server │ ├── index.js │ ├── views │ │ └── index.ejs │ └── app.prod.js │ └── scripts │ └── test.js ├── chapter-11 ├── chunked_with_redux │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── components │ │ │ ├── Counter │ │ │ │ ├── actionTypes.js │ │ │ │ ├── index.js │ │ │ │ ├── actions.js │ │ │ │ ├── reducer.js │ │ │ │ └── view.js │ │ │ └── TopMenu │ │ │ │ └── index.js │ │ ├── pages │ │ │ ├── About.js │ │ │ ├── NotFound.js │ │ │ ├── App.js │ │ │ ├── CounterPage.js │ │ │ └── Home.js │ │ ├── index.js │ │ ├── enhancer │ │ │ └── reset.js │ │ └── Store.js │ ├── config │ │ ├── jest │ │ │ ├── fileTransform.js │ │ │ └── cssTransform.js │ │ └── polyfills.js │ └── scripts │ │ └── test.js ├── react_router_basic │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── pages │ │ │ ├── About.js │ │ │ ├── NotFound.js │ │ │ ├── App.js │ │ │ └── Home.js │ │ ├── index.js │ │ ├── components │ │ │ └── TopMenu │ │ │ │ └── index.js │ │ ├── Store.js │ │ └── Routes.js │ ├── .gitignore │ └── package.json └── react_router_chunked │ ├── public │ └── favicon.ico │ ├── src │ ├── pages │ │ ├── About.js │ │ ├── NotFound.js │ │ ├── App.js │ │ └── Home.js │ ├── index.js │ ├── components │ │ └── TopMenu │ │ │ └── index.js │ └── Store.js │ ├── config │ ├── jest │ │ ├── fileTransform.js │ │ └── cssTransform.js │ └── polyfills.js │ └── scripts │ └── test.js ├── chapter-06 ├── hoc │ ├── src │ │ ├── proxy │ │ │ ├── removeUserPropHOC.js │ │ │ ├── AddNewPropsHOC.js │ │ │ ├── StyleHOC.js │ │ │ └── RefsHOC.js │ │ └── inheritance │ │ │ ├── cacheHOC.js │ │ │ ├── OnlyForLoggedinHOC.js │ │ │ ├── ModifyPropsHOC.js │ │ │ └── removeUserPropHOC.js │ ├── package.json │ └── test │ │ ├── proxy │ │ ├── RefsHOC.test.js │ │ ├── removeUserPropHOC.test.js │ │ ├── StyleHOC.test.js │ │ ├── AddNewPropsHOC.test.js │ │ └── ConnectHOC.test.js │ │ └── inheritance │ │ ├── removeUserPropHOC.test.js │ │ ├── OnlyForLoggedInHOC.test.js │ │ └── ModifyPropsHOC.test.js └── function_as_child │ ├── src │ ├── AddUserProp.js │ └── CountDown.js │ ├── test │ ├── AddUserProp.test.js │ └── countDown.test.js │ └── package.json ├── README.md └── .gitignore /chapter-03/flux/config/jest/CSSStub.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /chapter-02/controlpanel/config/jest/CSSStub.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /chapter-03/react-redux/config/jest/CSSStub.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /chapter-03/redux_basic/config/jest/CSSStub.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /chapter-01/first_react_app/config/jest/CSSStub.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /chapter-03/flux/config/jest/FileStub.js: -------------------------------------------------------------------------------- 1 | module.exports = "test-file-stub"; 2 | -------------------------------------------------------------------------------- /chapter-03/redux_smart_dumb/config/jest/CSSStub.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /chapter-03/redux_with_context/config/jest/CSSStub.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /chapter-02/controlpanel/config/jest/FileStub.js: -------------------------------------------------------------------------------- 1 | module.exports = "test-file-stub"; 2 | -------------------------------------------------------------------------------- /chapter-02/controlpanel_with_summary/config/jest/CSSStub.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /chapter-03/react-redux/config/jest/FileStub.js: -------------------------------------------------------------------------------- 1 | module.exports = "test-file-stub"; 2 | -------------------------------------------------------------------------------- /chapter-03/redux_basic/config/jest/FileStub.js: -------------------------------------------------------------------------------- 1 | module.exports = "test-file-stub"; 2 | -------------------------------------------------------------------------------- /chapter-04/todo/src/filter/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const SET_FILTER = 'FILTER/SET'; 2 | -------------------------------------------------------------------------------- /chapter-01/first_react_app/config/jest/FileStub.js: -------------------------------------------------------------------------------- 1 | module.exports = "test-file-stub"; 2 | -------------------------------------------------------------------------------- /chapter-03/redux_smart_dumb/config/jest/FileStub.js: -------------------------------------------------------------------------------- 1 | module.exports = "test-file-stub"; 2 | -------------------------------------------------------------------------------- /chapter-03/redux_with_context/config/jest/FileStub.js: -------------------------------------------------------------------------------- 1 | module.exports = "test-file-stub"; 2 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/filter/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const SET_FILTER = 'FILTER/SET'; 2 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/filter/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const SET_FILTER = 'FILTER/SET'; 2 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/filter/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const SET_FILTER = 'FILTER/SET'; 2 | -------------------------------------------------------------------------------- /chapter-02/controlpanel_with_summary/config/jest/FileStub.js: -------------------------------------------------------------------------------- 1 | module.exports = "test-file-stub"; 2 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/filter/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const SET_FILTER = 'FILTER/SET'; 2 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/filter/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const SET_FILTER = 'FILTER/SET'; 2 | -------------------------------------------------------------------------------- /chapter-03/flux/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-07/weather_redux/src/city_selector/index.js: -------------------------------------------------------------------------------- 1 | import view from './view.js'; 2 | 3 | export {view}; 4 | -------------------------------------------------------------------------------- /chapter-03/flux/src/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | import {Dispatcher} from 'flux'; 2 | 3 | export default new Dispatcher(); 4 | -------------------------------------------------------------------------------- /chapter-07/weather_redux_improved/src/city_selector/index.js: -------------------------------------------------------------------------------- 1 | import view from './view.js'; 2 | 3 | export {view}; 4 | -------------------------------------------------------------------------------- /chapter-01/first_react_app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-02/controlpanel/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-03/react-redux/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-03/redux_basic/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-07/weather_react/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-03/flux/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-03/flux/public/favicon.ico -------------------------------------------------------------------------------- /chapter-03/flux/src/ActionTypes.js: -------------------------------------------------------------------------------- 1 | export const INCREMENT = 'increment'; 2 | 3 | export const DECREMENT = 'decrement'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-03/redux_smart_dumb/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-03/redux_with_context/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-04/todo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-04/todo/public/favicon.ico -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/src/city_selector/index.js: -------------------------------------------------------------------------------- 1 | import view from './view.js'; 2 | 3 | export {view}; 4 | -------------------------------------------------------------------------------- /chapter-02/controlpanel_with_summary/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /chapter-03/react-redux/src/ActionTypes.js: -------------------------------------------------------------------------------- 1 | export const INCREMENT = 'increment'; 2 | 3 | export const DECREMENT = 'decrement'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-03/redux_basic/src/ActionTypes.js: -------------------------------------------------------------------------------- 1 | export const INCREMENT = 'increment'; 2 | 3 | export const DECREMENT = 'decrement'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-05/todo_perf/public/favicon.ico -------------------------------------------------------------------------------- /chapter-02/controlpanel/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-02/controlpanel/public/favicon.ico -------------------------------------------------------------------------------- /chapter-03/react-redux/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-03/react-redux/public/favicon.ico -------------------------------------------------------------------------------- /chapter-03/redux_basic/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-03/redux_basic/public/favicon.ico -------------------------------------------------------------------------------- /chapter-03/redux_smart_dumb/src/ActionTypes.js: -------------------------------------------------------------------------------- 1 | export const INCREMENT = 'increment'; 2 | 3 | export const DECREMENT = 'decrement'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-03/redux_with_context/src/ActionTypes.js: -------------------------------------------------------------------------------- 1 | export const INCREMENT = 'increment'; 2 | 3 | export const DECREMENT = 'decrement'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-04/todo/src/constants.js: -------------------------------------------------------------------------------- 1 | export const FilterTypes = { 2 | ALL: '全部', 3 | COMPLETED: '已完成', 4 | UNCOMPLETED: '未完成' 5 | } 6 | 7 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-12/isomorphic/public/favicon.ico -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/index.js: -------------------------------------------------------------------------------- 1 | import {renderRoutes} from './Routes.js'; 2 | 3 | renderRoutes(document.getElementById('root')); 4 | 5 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/constants.js: -------------------------------------------------------------------------------- 1 | export const FilterTypes = { 2 | ALL: '全部', 3 | COMPLETED: '已完成', 4 | UNCOMPLETED: '未完成' 5 | } 6 | 7 | -------------------------------------------------------------------------------- /chapter-07/weather_react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-07/weather_react/public/favicon.ico -------------------------------------------------------------------------------- /chapter-07/weather_redux/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-07/weather_redux/public/favicon.ico -------------------------------------------------------------------------------- /chapter-10/todo_animated/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-10/todo_animated/public/favicon.ico -------------------------------------------------------------------------------- /chapter-12/express_server/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-12/express_server/public/favicon.ico -------------------------------------------------------------------------------- /chapter-01/first_react_app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-01/first_react_app/public/favicon.ico -------------------------------------------------------------------------------- /chapter-03/redux_smart_dumb/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-03/redux_smart_dumb/public/favicon.ico -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/constants.js: -------------------------------------------------------------------------------- 1 | export const FilterTypes = { 2 | ALL: '全部', 3 | COMPLETED: '已完成', 4 | UNCOMPLETED: '未完成' 5 | } 6 | 7 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-10/todo_react_motion/public/favicon.ico -------------------------------------------------------------------------------- /chapter-03/redux_with_context/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-03/redux_with_context/public/favicon.ico -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-05/todo_with_selector/public/favicon.ico -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/constants.js: -------------------------------------------------------------------------------- 1 | export const FilterTypes = { 2 | ALL: '全部', 3 | COMPLETED: '已完成', 4 | UNCOMPLETED: '未完成' 5 | } 6 | 7 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/constants.js: -------------------------------------------------------------------------------- 1 | export const FilterTypes = { 2 | ALL: '全部', 3 | COMPLETED: '已完成', 4 | UNCOMPLETED: '未完成' 5 | } 6 | 7 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-11/chunked_with_redux/public/favicon.ico -------------------------------------------------------------------------------- /chapter-11/react_router_basic/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-11/react_router_basic/public/favicon.ico -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/constants.js: -------------------------------------------------------------------------------- 1 | export const FilterTypes = { 2 | ALL: '全部', 3 | COMPLETED: '已完成', 4 | UNCOMPLETED: '未完成' 5 | } 6 | 7 | -------------------------------------------------------------------------------- /chapter-07/weather_redux/src/weather/status.js: -------------------------------------------------------------------------------- 1 | export const LOADING = 'loading'; 2 | export const SUCCESS = 'success'; 3 | export const FAILURE = 'failure'; 4 | -------------------------------------------------------------------------------- /chapter-07/weather_redux_improved/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-07/weather_redux_improved/public/favicon.ico -------------------------------------------------------------------------------- /chapter-11/react_router_chunked/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-11/react_router_chunked/public/favicon.ico -------------------------------------------------------------------------------- /chapter-02/controlpanel_with_summary/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-02/controlpanel_with_summary/public/favicon.ico -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-04/todo_controlled_component/public/favicon.ico -------------------------------------------------------------------------------- /chapter-07/weather_redux/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | 7 | div { 8 | margin: 10px; 9 | } 10 | -------------------------------------------------------------------------------- /chapter-07/weather_redux_improved/src/weather/status.js: -------------------------------------------------------------------------------- 1 | export const LOADING = 'loading'; 2 | export const SUCCESS = 'success'; 3 | export const FAILURE = 'failure'; 4 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/components/Counter/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const INCREMENT = 'counter/increment'; 2 | 3 | export const DECREMENT = 'counter/decrement'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/components/Counter/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const INCREMENT = 'counter/increment'; 2 | 3 | export const DECREMENT = 'counter/decrement'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-04/todo/src/todos/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const ADD_TODO = 'TODO/ADD'; 2 | export const TOGGLE_TODO = 'TODO/TOGGLE'; 3 | export const REMOVE_TODO = 'TODO/REMOVE'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/src/weather/status.js: -------------------------------------------------------------------------------- 1 | export const LOADING = 'loading'; 2 | export const SUCCESS = 'success'; 3 | export const FAILURE = 'failure'; 4 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/components/Counter/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const INCREMENT = 'counter/increment'; 2 | 3 | export const DECREMENT = 'counter/decrement'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/todos/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const ADD_TODO = 'TODO/ADD'; 2 | export const TOGGLE_TODO = 'TODO/TOGGLE'; 3 | export const REMOVE_TODO = 'TODO/REMOVE'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-07/weather_redux_improved/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | 7 | div { 8 | margin: 10px; 9 | } 10 | -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mocheng/react-and-redux/HEAD/chapter-09/weather_with_promise_middleware/public/favicon.ico -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/todos/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const ADD_TODO = 'TODO/ADD'; 2 | export const TOGGLE_TODO = 'TODO/TOGGLE'; 3 | export const REMOVE_TODO = 'TODO/REMOVE'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/todos/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const ADD_TODO = 'TODO/ADD'; 2 | export const TOGGLE_TODO = 'TODO/TOGGLE'; 3 | export const REMOVE_TODO = 'TODO/REMOVE'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | 7 | div { 8 | margin: 10px; 9 | } 10 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/todos/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const ADD_TODO = 'TODO/ADD'; 2 | export const TOGGLE_TODO = 'TODO/TOGGLE'; 3 | export const REMOVE_TODO = 'TODO/REMOVE'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-04/todo/src/filter/actions.js: -------------------------------------------------------------------------------- 1 | import {SET_FILTER} from './actionTypes.js'; 2 | 3 | export const setFilter = filterType => ({ 4 | type: SET_FILTER, 5 | filter: filterType 6 | }); 7 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/todos/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const ADD_TODO = 'TODO/ADD'; 2 | export const TOGGLE_TODO = 'TODO/TOGGLE'; 3 | export const REMOVE_TODO = 'TODO/REMOVE'; 4 | 5 | -------------------------------------------------------------------------------- /chapter-04/todo/src/todos/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './views/todos.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/filter/actions.js: -------------------------------------------------------------------------------- 1 | import {SET_FILTER} from './actionTypes.js'; 2 | 3 | export const setFilter = filterType => ({ 4 | type: SET_FILTER, 5 | filter: filterType 6 | }); 7 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/filter/actions.js: -------------------------------------------------------------------------------- 1 | import {SET_FILTER} from './actionTypes.js'; 2 | 3 | export const setFilter = filterType => ({ 4 | type: SET_FILTER, 5 | filter: filterType 6 | }); 7 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/pages/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const About = () => { 4 | return ( 5 |
About
6 | ); 7 | }; 8 | 9 | export default About; 10 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/pages/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const About = () => { 4 | return ( 5 |
About
6 | ); 7 | }; 8 | 9 | export default About; 10 | -------------------------------------------------------------------------------- /chapter-04/todo/src/filter/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './views/filters.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/todos/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './views/todos.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/filter/actions.js: -------------------------------------------------------------------------------- 1 | import {SET_FILTER} from './actionTypes.js'; 2 | 3 | export const setFilter = filterType => ({ 4 | type: SET_FILTER, 5 | filter: filterType 6 | }); 7 | -------------------------------------------------------------------------------- /chapter-07/weather_redux/src/weather/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './view.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/filter/actions.js: -------------------------------------------------------------------------------- 1 | import {SET_FILTER} from './actionTypes.js'; 2 | 3 | export const setFilter = filterType => ({ 4 | type: SET_FILTER, 5 | filter: filterType 6 | }); 7 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/pages/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const About = () => { 4 | return ( 5 |
About
6 | ); 7 | }; 8 | 9 | export default About; 10 | -------------------------------------------------------------------------------- /chapter-11/react_router_basic/src/pages/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const About = () => { 4 | return ( 5 |
About
6 | ); 7 | }; 8 | 9 | export default About; 10 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/filter/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './views/filters.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/todos/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './views/todos.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-11/react_router_chunked/src/pages/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const About = () => { 4 | return ( 5 |
About
6 | ); 7 | }; 8 | 9 | export default About; 10 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/filter/actions.js: -------------------------------------------------------------------------------- 1 | import {SET_FILTER} from './actionTypes.js'; 2 | 3 | export const setFilter = filterType => ({ 4 | type: SET_FILTER, 5 | filter: filterType 6 | }); 7 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/todos/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './views/todos.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-07/weather_redux_improved/src/weather/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './view.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/filter/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './views/filters.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/filter/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './views/filters.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/todos/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './views/todos.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/pages/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NotFound = () => { 4 | return ( 5 |
404: Not Found
6 | ); 7 | }; 8 | 9 | export default NotFound; 10 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/todos/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './views/todos.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/filter/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './views/filters.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/pages/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NotFound = () => { 4 | return ( 5 |
404: Not Found
6 | ); 7 | }; 8 | 9 | export default NotFound; 10 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/filter/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './views/filters.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-07/weather_redux/src/weather/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const FETCH_STARTED = 'WEATHER/FETCH_STARTED'; 2 | export const FETCH_SUCCESS = 'WEATHER/FETCH_SUCCESS'; 3 | export const FETCH_FAILURE = 'WEATHER/FETCH_FAILURE'; 4 | -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/src/weather/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view from './view.js'; 4 | 5 | export {actions, reducer, view}; 6 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/pages/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NotFound = () => { 4 | return ( 5 |
404: Not Found
6 | ); 7 | }; 8 | 9 | export default NotFound; 10 | -------------------------------------------------------------------------------- /chapter-11/react_router_basic/src/pages/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NotFound = () => { 4 | return ( 5 |
404: Not Found
6 | ); 7 | }; 8 | 9 | export default NotFound; 10 | -------------------------------------------------------------------------------- /chapter-11/react_router_chunked/src/pages/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NotFound = () => { 4 | return ( 5 |
404: Not Found
6 | ); 7 | }; 8 | 9 | export default NotFound; 10 | -------------------------------------------------------------------------------- /chapter-07/weather_redux_improved/src/weather/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const FETCH_STARTED = 'WEATHER/FETCH_STARTED'; 2 | export const FETCH_SUCCESS = 'WEATHER/FETCH_SUCCESS'; 3 | export const FETCH_FAILURE = 'WEATHER/FETCH_FAILURE'; 4 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/components/Counter/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view, {stateKey} from './view.js'; 4 | 5 | export {actions, reducer, view, stateKey}; 6 | -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/src/weather/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const FETCH_STARTED = 'WEATHER/FETCH_STARTED'; 2 | export const FETCH_SUCCESS = 'WEATHER/FETCH_SUCCESS'; 3 | export const FETCH_FAILURE = 'WEATHER/FETCH_FAILURE'; 4 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/components/Counter/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view, {stateKey} from './view.js'; 4 | 5 | export {actions, reducer, view, stateKey}; 6 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/components/Counter/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions.js'; 2 | import reducer from './reducer.js'; 3 | import view, {stateKey} from './view.js'; 4 | 5 | export {actions, reducer, view, stateKey}; 6 | -------------------------------------------------------------------------------- /chapter-01/jquery_solution/clickCounter.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $('#clickMe').click(function() { 3 | var clickCounter = $('#clickCount'); 4 | var count = parseInt(clickCounter.text(), 10); 5 | clickCounter.text(count+1); 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /chapter-07/weather_react/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import Weather from './Weather'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /chapter-05/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /chapter-01/first_react_app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /chapter-02/controlpanel/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import ControlPanel from './ControlPanel'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /chapter-03/flux/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /chapter-03/flux/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import ControlPanel from './views/ControlPanel'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /chapter-04/todo/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /chapter-01/first_react_app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import ClickCounter from './ClickCounter'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /chapter-03/redux_basic/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import ControlPanel from './views/ControlPanel'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/components/Counter/actions.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from './actionTypes.js'; 2 | 3 | export const increment = () => ({ 4 | type: ActionTypes.INCREMENT 5 | }); 6 | 7 | export const decrement = () => ({ 8 | type: ActionTypes.DECREMENT 9 | }); 10 | -------------------------------------------------------------------------------- /chapter-01/first_react_app/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /chapter-02/controlpanel/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /chapter-03/redux_smart_dumb/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import ControlPanel from './views/ControlPanel'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /chapter-07/weather_react/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /chapter-07/weather_redux/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/components/Counter/actions.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from './actionTypes.js'; 2 | 3 | export const increment = () => ({ 4 | type: ActionTypes.INCREMENT 5 | }); 6 | 7 | export const decrement = () => ({ 8 | type: ActionTypes.DECREMENT 9 | }); 10 | -------------------------------------------------------------------------------- /chapter-02/controlpanel_with_summary/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import ControlPanel from './ControlPanel'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/components/Counter/actions.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from './actionTypes.js'; 2 | 3 | export const increment = () => ({ 4 | type: ActionTypes.INCREMENT 5 | }); 6 | 7 | export const decrement = () => ({ 8 | type: ActionTypes.DECREMENT 9 | }); 10 | -------------------------------------------------------------------------------- /chapter-11/react_router_basic/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /chapter-02/controlpanel_with_summary/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /chapter-07/weather_redux_improved/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /chapter-03/react-redux/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore} from 'redux'; 2 | import reducer from './Reducer.js'; 3 | 4 | const initValues = { 5 | 'First': 0, 6 | 'Second': 10, 7 | 'Third': 20 8 | }; 9 | 10 | const store = createStore(reducer, initValues); 11 | 12 | export default store; 13 | -------------------------------------------------------------------------------- /chapter-03/redux_basic/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore} from 'redux'; 2 | import reducer from './Reducer.js'; 3 | 4 | const initValues = { 5 | 'First': 0, 6 | 'Second': 10, 7 | 'Third': 20 8 | }; 9 | 10 | const store = createStore(reducer, initValues); 11 | 12 | export default store; 13 | -------------------------------------------------------------------------------- /chapter-03/redux_smart_dumb/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore} from 'redux'; 2 | import reducer from './Reducer.js'; 3 | 4 | const initValues = { 5 | 'First': 0, 6 | 'Second': 10, 7 | 'Third': 20 8 | }; 9 | 10 | const store = createStore(reducer, initValues); 11 | 12 | export default store; 13 | -------------------------------------------------------------------------------- /chapter-03/redux_with_context/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore} from 'redux'; 2 | import reducer from './Reducer.js'; 3 | 4 | const initValues = { 5 | 'First': 0, 6 | 'Second': 10, 7 | 'Third': 20 8 | }; 9 | 10 | const store = createStore(reducer, initValues); 11 | 12 | export default store; 13 | -------------------------------------------------------------------------------- /chapter-04/todo/src/TodoApp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {view as Todos} from './todos/'; 3 | import {view as Filter} from './filter/'; 4 | 5 | function TodoApp() { 6 | return ( 7 |
8 | 9 | 10 |
11 | ); 12 | } 13 | 14 | export default TodoApp; 15 | -------------------------------------------------------------------------------- /chapter-06/hoc/src/proxy/removeUserPropHOC.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function removeUserProp(WrappedComponent) { 4 | return function newRender(props) { 5 | const {user, ...otherProps} = props; 6 | return 7 | } 8 | } 9 | 10 | export default removeUserProp; 11 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/pages/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {view as TopMenu} from '../components/TopMenu'; 4 | 5 | const App = ({children}) => { 6 | return ( 7 |
8 | 9 |
{children}
10 |
11 | ); 12 | }; 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/TodoApp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {view as Todos} from './todos/'; 3 | import {view as Filter} from './filter/'; 4 | 5 | function TodoApp() { 6 | return ( 7 |
8 | 9 | 10 |
11 | ); 12 | } 13 | 14 | export default TodoApp; 15 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/TodoApp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {view as Todos} from './todos/'; 3 | import {view as Filter} from './filter/'; 4 | 5 | function TodoApp() { 6 | return ( 7 |
8 | 9 | 10 |
11 | ); 12 | } 13 | 14 | export default TodoApp; 15 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/pages/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {view as TopMenu} from '../components/TopMenu'; 4 | 5 | const App = ({children}) => { 6 | return ( 7 |
8 | 9 |
{children}
10 |
11 | ); 12 | }; 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /chapter-11/react_router_basic/src/pages/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {view as TopMenu} from '../components/TopMenu'; 4 | 5 | const App = ({children}) => { 6 | return ( 7 |
8 | 9 |
{children}
10 |
11 | ); 12 | }; 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/pages/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {view as TopMenu} from '../components/TopMenu'; 4 | 5 | const App = ({children}) => { 6 | return ( 7 |
8 | 9 |
{children}
10 |
11 | ); 12 | }; 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/TodoApp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {view as Todos} from './todos/'; 3 | import {view as Filter} from './filter/'; 4 | 5 | function TodoApp() { 6 | return ( 7 |
8 | 9 | 10 |
11 | ); 12 | } 13 | 14 | export default TodoApp; 15 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/TodoApp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {view as Todos} from './todos/'; 3 | import {view as Filter} from './filter/'; 4 | 5 | function TodoApp() { 6 | return ( 7 |
8 | 9 | 10 |
11 | ); 12 | } 13 | 14 | export default TodoApp; 15 | -------------------------------------------------------------------------------- /chapter-11/react_router_chunked/src/pages/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {view as TopMenu} from '../components/TopMenu'; 4 | 5 | const App = ({children}) => { 6 | return ( 7 |
8 | 9 |
{children}
10 |
11 | ); 12 | }; 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/TodoApp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {view as Todos} from './todos/'; 3 | import {view as Filter} from './filter/'; 4 | 5 | function TodoApp() { 6 | return ( 7 |
8 | 9 | 10 |
11 | ); 12 | } 13 | 14 | export default TodoApp; 15 | -------------------------------------------------------------------------------- /chapter-06/hoc/src/inheritance/cacheHOC.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const cacheHOC = (WrappedComponent) => { 4 | return class NewComponent extends WrappedComponent { 5 | shouldComponentUpdate(nextProps, nextState) { 6 | return nextProps.useCache; 7 | } 8 | } 9 | } 10 | 11 | export default onlyForLoggedinHOC; 12 | -------------------------------------------------------------------------------- /chapter-04/todo/src/filter/reducer.js: -------------------------------------------------------------------------------- 1 | import {SET_FILTER} from './actionTypes.js'; 2 | import {FilterTypes} from '../constants.js' 3 | 4 | export default (state = FilterTypes.ALL, action) => { 5 | switch(action.type) { 6 | case SET_FILTER: { 7 | return action.filter; 8 | } 9 | default: 10 | return state; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter-04/todo/src/todos/views/todos.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AddTodo from './addTodo.js'; 3 | import TodoList from './todoList.js'; 4 | 5 | import './style.css'; 6 | 7 | export default () => { 8 | return ( 9 |
10 | 11 | 12 |
13 | ); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /chapter-06/hoc/src/proxy/AddNewPropsHOC.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const addNewProps = (WrappedComponent, newProps) => { 4 | return class WrappingComponent extends React.Component { 5 | render() { 6 | return 7 | } 8 | } 9 | } 10 | 11 | export default addNewProps; 12 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/filter/reducer.js: -------------------------------------------------------------------------------- 1 | import {SET_FILTER} from './actionTypes.js'; 2 | import {FilterTypes} from '../constants.js' 3 | 4 | export default (state = FilterTypes.ALL, action) => { 5 | switch(action.type) { 6 | case SET_FILTER: { 7 | return action.filter; 8 | } 9 | default: 10 | return state; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/todos/views/todos.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AddTodo from './addTodo.js'; 3 | import TodoList from './todoList.js'; 4 | 5 | import './style.css'; 6 | 7 | export default () => { 8 | return ( 9 |
10 | 11 | 12 |
13 | ); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/todos/views/todos.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AddTodo from './addTodo.js'; 3 | import TodoList from './todoList.js'; 4 | 5 | import './style.css'; 6 | 7 | export default () => { 8 | return ( 9 |
10 | 11 | 12 |
13 | ); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/components/Counter/reducer.js: -------------------------------------------------------------------------------- 1 | import {INCREMENT, DECREMENT} from './actionTypes.js'; 2 | 3 | export default (state = {}, action) => { 4 | switch (action.type) { 5 | case INCREMENT: 6 | return state + 1; 7 | case DECREMENT: 8 | return state - 1; 9 | default: 10 | return state 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/components/Counter/reducer.js: -------------------------------------------------------------------------------- 1 | import {INCREMENT, DECREMENT} from './actionTypes.js'; 2 | 3 | export default (state = {}, action) => { 4 | switch (action.type) { 5 | case INCREMENT: 6 | return state + 1; 7 | case DECREMENT: 8 | return state - 1; 9 | default: 10 | return state 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter-04/todo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Provider} from 'react-redux'; 4 | import TodoApp from './TodoApp'; 5 | 6 | import store from './Store.js'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); 14 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/todos/views/todos.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AddTodo from './addTodo.js'; 3 | import TodoList from './todoList.js'; 4 | 5 | import './style.css'; 6 | 7 | export default () => { 8 | return ( 9 |
10 | 11 | 12 |
13 | ); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/filter/reducer.js: -------------------------------------------------------------------------------- 1 | import {SET_FILTER} from './actionTypes.js'; 2 | import {FilterTypes} from '../constants.js' 3 | 4 | export default (state = FilterTypes.ALL, action) => { 5 | switch(action.type) { 6 | case SET_FILTER: { 7 | return action.filter; 8 | } 9 | default: 10 | return state; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/todos/views/todos.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AddTodo from './addTodo.js'; 3 | import TodoList from './todoList.js'; 4 | 5 | import './style.css'; 6 | 7 | export default () => { 8 | return ( 9 |
10 | 11 | 12 |
13 | ); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/components/Counter/reducer.js: -------------------------------------------------------------------------------- 1 | import {INCREMENT, DECREMENT} from './actionTypes.js'; 2 | 3 | export default (state = {}, action) => { 4 | switch (action.type) { 5 | case INCREMENT: 6 | return state + 1; 7 | case DECREMENT: 8 | return state - 1; 9 | default: 10 | return state 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Provider} from 'react-redux'; 4 | import TodoApp from './TodoApp'; 5 | 6 | import store from './Store.js'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); 14 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/filter/reducer.js: -------------------------------------------------------------------------------- 1 | import {SET_FILTER} from './actionTypes.js'; 2 | import {FilterTypes} from '../constants.js' 3 | 4 | export default (state = FilterTypes.ALL, action) => { 5 | switch(action.type) { 6 | case SET_FILTER: { 7 | return action.filter; 8 | } 9 | default: 10 | return state; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/todos/views/todoItem.css: -------------------------------------------------------------------------------- 1 | .fade-enter{ 2 | opacity: 0.01; 3 | } 4 | 5 | .fade-enter.fade-enter-active { 6 | opacity: 1; 7 | transition: opacity 500ms ease-in; 8 | } 9 | 10 | .fade-leave { 11 | opacity: 1; 12 | } 13 | 14 | .fade-leave.fade-leave-active { 15 | opacity: 0.01; 16 | transition: opacity 200ms ease-in; 17 | } 18 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/filter/reducer.js: -------------------------------------------------------------------------------- 1 | import {SET_FILTER} from './actionTypes.js'; 2 | import {FilterTypes} from '../constants.js' 3 | 4 | export default (state = FilterTypes.ALL, action) => { 5 | switch(action.type) { 6 | case SET_FILTER: { 7 | return action.filter; 8 | } 9 | default: 10 | return state; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/filter/reducer.js: -------------------------------------------------------------------------------- 1 | import {SET_FILTER} from './actionTypes.js'; 2 | import {FilterTypes} from '../constants.js' 3 | 4 | export default (state = FilterTypes.ALL, action) => { 5 | switch(action.type) { 6 | case SET_FILTER: { 7 | return action.filter; 8 | } 9 | default: 10 | return state; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/todos/views/todos.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AddTodo from './addTodo.js'; 3 | import TodoList from './todoList.js'; 4 | 5 | import './style.css'; 6 | 7 | export default () => { 8 | return ( 9 |
10 | 11 | 12 |
13 | ); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Provider} from 'react-redux'; 4 | import TodoApp from './TodoApp'; 5 | 6 | import store from './Store.js'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); 14 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Provider} from 'react-redux'; 4 | import TodoApp from './TodoApp'; 5 | 6 | import store from './Store.js'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); 14 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Provider} from 'react-redux'; 4 | import TodoApp from './TodoApp'; 5 | 6 | import store from './Store.js'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); 14 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Provider} from 'react-redux'; 4 | import TodoApp from './TodoApp'; 5 | 6 | import store from './Store.js'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); 14 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | // This is a custom Jest transformer turning file imports into filenames. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process(src, filename) { 8 | return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /chapter-07/weather_redux/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Provider} from 'react-redux'; 4 | import App from './App.js'; 5 | 6 | import './index.css'; 7 | 8 | import store from './Store.js' 9 | 10 | ReactDOM.render( 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ); 16 | -------------------------------------------------------------------------------- /chapter-12/express_server/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | // This is a custom Jest transformer turning file imports into filenames. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process(src, filename) { 8 | return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /chapter-07/weather_redux_improved/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Provider} from 'react-redux'; 4 | import App from './App.js'; 5 | 6 | import './index.css'; 7 | 8 | import store from './Store.js' 9 | 10 | ReactDOM.render( 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ); 16 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | // This is a custom Jest transformer turning file imports into filenames. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process(src, filename) { 8 | return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/pages/CounterPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {view as Counter, stateKey, reducer} from '../components/Counter'; 3 | 4 | const page = () => { 5 | return ( 6 |
7 |
Counter
8 | 9 |
10 | ); 11 | }; 12 | 13 | const initialState = 100; 14 | 15 | export {page, reducer, initialState, stateKey}; 16 | -------------------------------------------------------------------------------- /chapter-11/react_router_chunked/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | // This is a custom Jest transformer turning file imports into filenames. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process(src, filename) { 8 | return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import Routes from './Routes.js'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | 11 | /* 12 | ReactDOM.render( 13 | 14 | , 15 | 16 | document.getElementById('root') 17 | ); 18 | */ 19 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import Routes from './Routes.js'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | 11 | /* 12 | ReactDOM.render( 13 | 14 | , 15 | 16 | document.getElementById('root') 17 | ); 18 | */ 19 | -------------------------------------------------------------------------------- /chapter-11/react_router_basic/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import Routes from './Routes.js'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | 11 | /* 12 | ReactDOM.render( 13 | 14 | , 15 | 16 | document.getElementById('root') 17 | ); 18 | */ 19 | -------------------------------------------------------------------------------- /chapter-06/hoc/src/proxy/StyleHOC.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const styleHOC = (WrappedComponent, style) => { 4 | return class HOCComponent extends React.Component { 5 | render() { 6 | return ( 7 |
8 | 9 |
10 | ); 11 | } 12 | }; 13 | }; 14 | 15 | export default styleHOC; 16 | 17 | 18 | -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Provider} from 'react-redux'; 4 | import App from './App.js'; 5 | 6 | import './index.css'; 7 | 8 | import store from './Store.js' 9 | 10 | ReactDOM.render( 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ); 16 | -------------------------------------------------------------------------------- /chapter-11/react_router_chunked/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import Routes from './Routes.js'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | 11 | /* 12 | ReactDOM.render( 13 | 14 | , 15 | 16 | document.getElementById('root') 17 | ); 18 | */ 19 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {connect} from 'react-redux'; 3 | 4 | const Home = ({greetings}) => { 5 | return ( 6 |
7 |
Home
8 |
{greetings}
9 |
10 | ); 11 | }; 12 | 13 | const mapStateToProps = (state) => ({greetings: state.greetings}); 14 | 15 | export default connect(mapStateToProps)(Home); 16 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {connect} from 'react-redux'; 3 | 4 | const Home = ({greetings}) => { 5 | return ( 6 |
7 |
Home
8 |
{greetings}
9 |
10 | ); 11 | }; 12 | 13 | const mapStateToProps = (state) => ({greetings: state.greetings}); 14 | 15 | export default connect(mapStateToProps)(Home); 16 | -------------------------------------------------------------------------------- /chapter-07/weather_redux/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import {view as CitySelector} from './city_selector/'; 4 | import {view as Weather} from './weather/'; 5 | 6 | class App extends Component { 7 | render() { 8 | return ( 9 |
10 | 11 | 12 |
13 | ); 14 | } 15 | } 16 | 17 | export default App; 18 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {connect} from 'react-redux'; 3 | 4 | const Home = ({greetings}) => { 5 | return ( 6 |
7 |
Home
8 |
{greetings}
9 |
10 | ); 11 | }; 12 | 13 | const mapStateToProps = (state) => ({greetings: state.greetings}); 14 | 15 | export default connect(mapStateToProps)(Home); 16 | -------------------------------------------------------------------------------- /chapter-11/react_router_basic/src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {connect} from 'react-redux'; 3 | 4 | const Home = ({greetings}) => { 5 | return ( 6 |
7 |
Home
8 |
{greetings}
9 |
10 | ); 11 | }; 12 | 13 | const mapStateToProps = (state) => ({greetings: state.greetings}); 14 | 15 | export default connect(mapStateToProps)(Home); 16 | -------------------------------------------------------------------------------- /chapter-03/react-redux/src/Actions.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from './ActionTypes.js'; 2 | 3 | export const increment = (counterCaption) => { 4 | return { 5 | type: ActionTypes.INCREMENT, 6 | counterCaption: counterCaption 7 | }; 8 | }; 9 | 10 | export const decrement = (counterCaption) => { 11 | return { 12 | type: ActionTypes.DECREMENT, 13 | counterCaption: counterCaption 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /chapter-03/react-redux/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Provider} from 'react-redux'; 4 | 5 | import ControlPanel from './views/ControlPanel'; 6 | import store from './Store.js'; 7 | 8 | import './index.css'; 9 | 10 | ReactDOM.render( 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ); 16 | -------------------------------------------------------------------------------- /chapter-03/redux_basic/src/Actions.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from './ActionTypes.js'; 2 | 3 | export const increment = (counterCaption) => { 4 | return { 5 | type: ActionTypes.INCREMENT, 6 | counterCaption: counterCaption 7 | }; 8 | }; 9 | 10 | export const decrement = (counterCaption) => { 11 | return { 12 | type: ActionTypes.DECREMENT, 13 | counterCaption: counterCaption 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /chapter-06/hoc/src/inheritance/OnlyForLoggedinHOC.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const onlyForLoggedinHOC = (WrappedComponent) => { 4 | return class NewComponent extends WrappedComponent { 5 | render() { 6 | if (this.props.loggedIn) { 7 | return super.render(); 8 | } else { 9 | return null; 10 | } 11 | } 12 | } 13 | } 14 | 15 | export default onlyForLoggedinHOC; 16 | -------------------------------------------------------------------------------- /chapter-07/weather_redux_improved/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import {view as CitySelector} from './city_selector/'; 4 | import {view as Weather} from './weather/'; 5 | 6 | class App extends Component { 7 | render() { 8 | return ( 9 |
10 | 11 | 12 |
13 | ); 14 | } 15 | } 16 | 17 | export default App; 18 | -------------------------------------------------------------------------------- /chapter-11/react_router_chunked/src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {connect} from 'react-redux'; 3 | 4 | const Home = ({greetings}) => { 5 | return ( 6 |
7 |
Home
8 |
{greetings}
9 |
10 | ); 11 | }; 12 | 13 | const mapStateToProps = (state) => ({greetings: state.greetings}); 14 | 15 | export default connect(mapStateToProps)(Home); 16 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | // This is a custom Jest transformer turning style imports into empty objects. 2 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 3 | 4 | module.exports = { 5 | process() { 6 | return 'module.exports = {};'; 7 | }, 8 | getCacheKey(fileData, filename) { 9 | // The output is always the same. 10 | return 'cssTransform'; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /chapter-01/jquery_solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |
7 | Click Count: 0 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /chapter-03/redux_smart_dumb/src/Actions.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from './ActionTypes.js'; 2 | 3 | export const increment = (counterCaption) => { 4 | return { 5 | type: ActionTypes.INCREMENT, 6 | counterCaption: counterCaption 7 | }; 8 | }; 9 | 10 | export const decrement = (counterCaption) => { 11 | return { 12 | type: ActionTypes.DECREMENT, 13 | counterCaption: counterCaption 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /chapter-03/redux_with_context/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import ControlPanel from './views/ControlPanel'; 4 | import './index.css'; 5 | 6 | import store from './Store.js'; 7 | import Provider from './Provider.js'; 8 | 9 | ReactDOM.render( 10 | 11 | 12 | , 13 | document.getElementById('root') 14 | ); 15 | -------------------------------------------------------------------------------- /chapter-06/function_as_child/src/AddUserProp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const loggedinUser = 'mock user'; 4 | 5 | class AddUserProp extends React.Component { 6 | render() { 7 | const user = loggedinUser; 8 | return this.props.children(user) 9 | } 10 | } 11 | 12 | AddUserProp.propTypes = { 13 | children: React.PropTypes.func.isRequired 14 | } 15 | 16 | export default AddUserProp; 17 | 18 | 19 | -------------------------------------------------------------------------------- /chapter-12/express_server/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | // This is a custom Jest transformer turning style imports into empty objects. 2 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 3 | 4 | module.exports = { 5 | process() { 6 | return 'module.exports = {};'; 7 | }, 8 | getCacheKey(fileData, filename) { 9 | // The output is always the same. 10 | return 'cssTransform'; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/pages/CounterPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {view as Counter, stateKey, reducer} from '../components/Counter'; 3 | 4 | const page = () => { 5 | return ( 6 |
7 |
Counter
8 | 9 |
10 | ); 11 | }; 12 | 13 | const initState = () => Promise.resolve(100); 14 | 15 | export {page, reducer, initState, stateKey}; 16 | -------------------------------------------------------------------------------- /chapter-03/redux_with_context/src/Actions.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from './ActionTypes.js'; 2 | 3 | export const increment = (counterCaption) => { 4 | return { 5 | type: ActionTypes.INCREMENT, 6 | counterCaption: counterCaption 7 | }; 8 | }; 9 | 10 | export const decrement = (counterCaption) => { 11 | return { 12 | type: ActionTypes.DECREMENT, 13 | counterCaption: counterCaption 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import {view as CitySelector} from './city_selector/'; 4 | import {view as Weather} from './weather/'; 5 | 6 | class App extends Component { 7 | render() { 8 | return ( 9 |
10 | 11 | 12 |
13 | ); 14 | } 15 | } 16 | 17 | export default App; 18 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | // This is a custom Jest transformer turning style imports into empty objects. 2 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 3 | 4 | module.exports = { 5 | process() { 6 | return 'module.exports = {};'; 7 | }, 8 | getCacheKey(fileData, filename) { 9 | // The output is always the same. 10 | return 'cssTransform'; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /chapter-11/react_router_chunked/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | // This is a custom Jest transformer turning style imports into empty objects. 2 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 3 | 4 | module.exports = { 5 | process() { 6 | return 'module.exports = {};'; 7 | }, 8 | getCacheKey(fileData, filename) { 9 | // The output is always the same. 10 | return 'cssTransform'; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /chapter-04/todo/src/todos/actions.js: -------------------------------------------------------------------------------- 1 | import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO} from './actionTypes.js'; 2 | 3 | let nextTodoId = 0; 4 | 5 | export const addTodo = (text) => ({ 6 | type: ADD_TODO, 7 | completed: false, 8 | id: nextTodoId ++, 9 | text: text 10 | }); 11 | 12 | export const toggleTodo = (id) => ({ 13 | type: TOGGLE_TODO, 14 | id: id 15 | }); 16 | 17 | export const removeTodo = (id) => ({ 18 | type: REMOVE_TODO, 19 | id: id 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /chapter-03/react-redux/src/Reducer.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from './ActionTypes.js'; 2 | 3 | export default (state, action) => { 4 | const {counterCaption} = action; 5 | 6 | switch (action.type) { 7 | case ActionTypes.INCREMENT: 8 | return {...state, [counterCaption]: state[counterCaption] + 1}; 9 | case ActionTypes.DECREMENT: 10 | return {...state, [counterCaption]: state[counterCaption] - 1}; 11 | default: 12 | return state 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chapter-03/redux_basic/src/Reducer.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from './ActionTypes.js'; 2 | 3 | export default (state, action) => { 4 | const {counterCaption} = action; 5 | 6 | switch (action.type) { 7 | case ActionTypes.INCREMENT: 8 | return {...state, [counterCaption]: state[counterCaption] + 1}; 9 | case ActionTypes.DECREMENT: 10 | return {...state, [counterCaption]: state[counterCaption] - 1}; 11 | default: 12 | return state 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/todos/actions.js: -------------------------------------------------------------------------------- 1 | import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO} from './actionTypes.js'; 2 | 3 | let nextTodoId = 10; 4 | 5 | export const addTodo = (text) => ({ 6 | type: ADD_TODO, 7 | completed: false, 8 | id: nextTodoId ++, 9 | text: text 10 | }); 11 | 12 | export const toggleTodo = (id) => ({ 13 | type: TOGGLE_TODO, 14 | id: id 15 | }); 16 | 17 | export const removeTodo = (id) => ({ 18 | type: REMOVE_TODO, 19 | id: id 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /chapter-01/first_react_app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /chapter-03/redux_smart_dumb/src/Reducer.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from './ActionTypes.js'; 2 | 3 | export default (state, action) => { 4 | const {counterCaption} = action; 5 | 6 | switch (action.type) { 7 | case ActionTypes.INCREMENT: 8 | return {...state, [counterCaption]: state[counterCaption] + 1}; 9 | case ActionTypes.DECREMENT: 10 | return {...state, [counterCaption]: state[counterCaption] - 1}; 11 | default: 12 | return state 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/todos/actions.js: -------------------------------------------------------------------------------- 1 | import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO} from './actionTypes.js'; 2 | 3 | let nextTodoId = 10; 4 | 5 | export const addTodo = (text) => ({ 6 | type: ADD_TODO, 7 | completed: false, 8 | id: nextTodoId ++, 9 | text: text 10 | }); 11 | 12 | export const toggleTodo = (id) => ({ 13 | type: TOGGLE_TODO, 14 | id: id 15 | }); 16 | 17 | export const removeTodo = (id) => ({ 18 | type: REMOVE_TODO, 19 | id: id 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /chapter-03/redux_with_context/src/Reducer.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from './ActionTypes.js'; 2 | 3 | export default (state, action) => { 4 | const {counterCaption} = action; 5 | 6 | switch (action.type) { 7 | case ActionTypes.INCREMENT: 8 | return {...state, [counterCaption]: state[counterCaption] + 1}; 9 | case ActionTypes.DECREMENT: 10 | return {...state, [counterCaption]: state[counterCaption] - 1}; 11 | default: 12 | return state 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/todos/actions.js: -------------------------------------------------------------------------------- 1 | import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO} from './actionTypes.js'; 2 | 3 | let nextTodoId = 10; 4 | 5 | export const addTodo = (text) => ({ 6 | type: ADD_TODO, 7 | completed: false, 8 | id: nextTodoId ++, 9 | text: text 10 | }); 11 | 12 | export const toggleTodo = (id) => ({ 13 | type: TOGGLE_TODO, 14 | id: id 15 | }); 16 | 17 | export const removeTodo = (id) => ({ 18 | type: REMOVE_TODO, 19 | id: id 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /chapter-09/reset_enhancer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react": "^15.4.1", 7 | "react-addons-test-utils": "^15.4.1", 8 | "react-dom": "^15.4.1", 9 | "react-redux": "^5.0.1", 10 | "react-scripts": "0.8.4", 11 | "redux": "^3.6.0", 12 | "sinon": "^1.17.7" 13 | }, 14 | "dependencies": {}, 15 | "scripts": { 16 | "test": "react-scripts test --env=jsdom" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /chapter-10/animation_types/css3_sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/todos/actions.js: -------------------------------------------------------------------------------- 1 | import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO} from './actionTypes.js'; 2 | 3 | let nextTodoId = 10; 4 | 5 | export const addTodo = (text) => ({ 6 | type: ADD_TODO, 7 | completed: false, 8 | id: nextTodoId ++, 9 | text: text 10 | }); 11 | 12 | export const toggleTodo = (id) => ({ 13 | type: TOGGLE_TODO, 14 | id: id 15 | }); 16 | 17 | export const removeTodo = (id) => ({ 18 | type: REMOVE_TODO, 19 | id: id 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /chapter-03/flux/src/Actions.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from './ActionTypes.js'; 2 | import AppDispatcher from './AppDispatcher.js'; 3 | 4 | export const increment = (counterCaption) => { 5 | AppDispatcher.dispatch({ 6 | type: ActionTypes.INCREMENT, 7 | counterCaption: counterCaption 8 | }); 9 | }; 10 | 11 | export const decrement = (counterCaption) => { 12 | AppDispatcher.dispatch({ 13 | type: ActionTypes.DECREMENT, 14 | counterCaption: counterCaption 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/todos/actions.js: -------------------------------------------------------------------------------- 1 | import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO} from './actionTypes.js'; 2 | 3 | let nextTodoId = 0; 4 | 5 | export const addTodo = (text) => ({ 6 | type: ADD_TODO, 7 | completed: false, 8 | id: nextTodoId ++, 9 | text: text 10 | }); 11 | 12 | export const toggleTodo = (id) => ({ 13 | type: TOGGLE_TODO, 14 | id: id 15 | }); 16 | 17 | export const removeTodo = (id) => ({ 18 | type: REMOVE_TODO, 19 | id: id 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /chapter-09/promise_middleware/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react": "^15.4.1", 7 | "react-addons-test-utils": "^15.4.1", 8 | "react-dom": "^15.4.1", 9 | "react-redux": "^5.0.1", 10 | "react-scripts": "0.8.4", 11 | "redux": "^3.6.0", 12 | "sinon": "^1.17.7" 13 | }, 14 | "dependencies": {}, 15 | "scripts": { 16 | "test": "react-scripts test --env=jsdom" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /chapter-11/react_router_basic/src/components/TopMenu/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {Link} from 'react-router'; 4 | 5 | const liStyle = { 6 | display: 'inline-block', 7 | margin: '10px 20px' 8 | } 9 | 10 | const view = () => { 11 | return ( 12 |
13 |
    14 |
  • Home
  • 15 |
  • About
  • 16 |
17 |
18 | ); 19 | }; 20 | 21 | export {view}; 22 | -------------------------------------------------------------------------------- /chapter-03/redux_with_context/src/Provider.js: -------------------------------------------------------------------------------- 1 | import {PropTypes, Component} from 'react'; 2 | 3 | class Provider extends Component { 4 | 5 | getChildContext() { 6 | return { 7 | store: this.props.store 8 | }; 9 | } 10 | 11 | render() { 12 | return this.props.children; 13 | } 14 | 15 | } 16 | 17 | Provider.propTypes = { 18 | store: PropTypes.object.isRequired 19 | } 20 | 21 | Provider.childContextTypes = { 22 | store: PropTypes.object 23 | }; 24 | 25 | export default Provider; 26 | -------------------------------------------------------------------------------- /chapter-11/react_router_chunked/src/components/TopMenu/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {Link} from 'react-router'; 4 | 5 | const liStyle = { 6 | display: 'inline-block', 7 | margin: '10px 20px' 8 | } 9 | 10 | const view = () => { 11 | return ( 12 |
13 |
    14 |
  • Home
  • 15 |
  • About
  • 16 |
17 |
18 | ); 19 | }; 20 | 21 | export {view}; 22 | -------------------------------------------------------------------------------- /chapter-12/express_server/server/index.js: -------------------------------------------------------------------------------- 1 | const isProductionMode = (process.env.NODE_ENV === 'production'); 2 | const app = isProductionMode ? require('./app.prod.js'): require('./app.dev.js'); 3 | 4 | if (!isProductionMode) { 5 | process.env.NODE_ENV = 'development'; 6 | } 7 | 8 | const PORT = process.env.PORT || 9000; 9 | 10 | app.listen(PORT, function() { 11 | console.log('running in ' + (isProductionMode ? 'producition' : 'development') + ' mode'); 12 | console.log('listening on port: ' + PORT); 13 | }); 14 | -------------------------------------------------------------------------------- /chapter-07/weather_react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weather_react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "proxy": "http://www.weather.com.cn/", 6 | "devDependencies": { 7 | "react-scripts": "0.8.4" 8 | }, 9 | "dependencies": { 10 | "react": "^15.4.1", 11 | "react-dom": "^15.4.1" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter-12/express_server/server/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= title %> 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /chapter-09/promise_middleware/src/simple.js: -------------------------------------------------------------------------------- 1 | function isPromise(obj) { 2 | return obj && typeof obj.then === 'function'; 3 | } 4 | 5 | /* 6 | export default function promiseMiddleware({dispatch}) { 7 | return next => action => { 8 | return isPromise(action) ? action.then(dispatch) : next(action); 9 | } 10 | } 11 | */ 12 | 13 | export default function promiseMiddleware({dispatch}) { 14 | return function(next) { 15 | return function(action) { 16 | return isPromise(action) ? action.then(dispatch) : next(action); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter-04/todo/src/filter/views/filters.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from './link.js'; 3 | import {FilterTypes} from '../../constants.js' 4 | 5 | import './style.css'; 6 | 7 | const Filters = () => { 8 | return ( 9 |

10 | {FilterTypes.ALL} 11 | {FilterTypes.COMPLETED} 12 | {FilterTypes.UNCOMPLETED} 13 |

14 | ); 15 | }; 16 | 17 | export default Filters; 18 | -------------------------------------------------------------------------------- /chapter-06/function_as_child/test/AddUserProp.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {mount} from 'enzyme'; 3 | 4 | import AddUserProp from '../src/AddUserProp.js'; 5 | 6 | describe('AddUserProp', () => { 7 | it('should add user', () => { 8 | const wrapper = mount( 9 | 10 | { 11 | (user) =>
{user}
12 | } 13 |
14 | ); 15 | 16 | const userText = wrapper.find('div').text(); 17 | expect(userText).toEqual('mock user'); 18 | 19 | }); 20 | 21 | }); 22 | 23 | 24 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/filter/views/filters.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from './link.js'; 3 | import {FilterTypes} from '../../constants.js' 4 | 5 | import './style.css'; 6 | 7 | const Filters = () => { 8 | return ( 9 |

10 | {FilterTypes.ALL} 11 | {FilterTypes.COMPLETED} 12 | {FilterTypes.UNCOMPLETED} 13 |

14 | ); 15 | }; 16 | 17 | export default Filters; 18 | -------------------------------------------------------------------------------- /chapter-07/weather_redux/test/weather/reducer.test.js: -------------------------------------------------------------------------------- 1 | import * as actions from '../../src/weather/actions.js'; 2 | import * as actionTypes from '../../src/weather/actionTypes.js'; 3 | import * as Status from '../../src/weather/status.js'; 4 | import reducer from '../../src/weather/reducer.js'; 5 | 6 | describe('weather/reducer', () => { 7 | it('should return loading status', () => { 8 | const action = actions.fetchWeatherStarted(); 9 | 10 | const newState = reducer({}, action); 11 | 12 | expect(newState.status).toBe(Status.LOADING); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/filter/views/filters.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from './link.js'; 3 | import {FilterTypes} from '../../constants.js' 4 | 5 | import './style.css'; 6 | 7 | const Filters = () => { 8 | return ( 9 |

10 | {FilterTypes.ALL} 11 | {FilterTypes.COMPLETED} 12 | {FilterTypes.UNCOMPLETED} 13 |

14 | ); 15 | }; 16 | 17 | export default Filters; 18 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/components/TopMenu/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {Link} from 'react-router'; 4 | 5 | const liStyle = { 6 | display: 'inline-block', 7 | margin: '10px 20px' 8 | } 9 | 10 | const view = () => { 11 | return ( 12 |
13 |
    14 |
  • Home
  • 15 |
  • Counter
  • 16 |
  • About
  • 17 |
18 |
19 | ); 20 | }; 21 | 22 | export {view}; 23 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/filter/views/filters.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from './link.js'; 3 | import {FilterTypes} from '../../constants.js' 4 | 5 | import './style.css'; 6 | 7 | const Filters = () => { 8 | return ( 9 |

10 | {FilterTypes.ALL} 11 | {FilterTypes.COMPLETED} 12 | {FilterTypes.UNCOMPLETED} 13 |

14 | ); 15 | }; 16 | 17 | export default Filters; 18 | -------------------------------------------------------------------------------- /chapter-06/hoc/src/proxy/RefsHOC.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const refsHOC = (WrappedComponent) => { 4 | return class HOCComponent extends React.Component { 5 | constructor() { 6 | super(...arguments); 7 | 8 | this.linkRef = this.linkRef.bind(this); 9 | } 10 | 11 | linkRef(wrappedInstance) { 12 | this._root = wrappedInstance; 13 | } 14 | 15 | render() { 16 | const props = {...this.props, ref: this.linkRef}; 17 | return ; 18 | } 19 | }; 20 | }; 21 | 22 | export default refsHOC; 23 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/filter/views/filters.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from './link.js'; 3 | import {FilterTypes} from '../../constants.js' 4 | 5 | import './style.css'; 6 | 7 | const Filters = () => { 8 | return ( 9 |

10 | {FilterTypes.ALL} 11 | {FilterTypes.COMPLETED} 12 | {FilterTypes.UNCOMPLETED} 13 |

14 | ); 15 | }; 16 | 17 | export default Filters; 18 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/components/TopMenu/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {Link} from 'react-router'; 4 | 5 | const liStyle = { 6 | display: 'inline-block', 7 | margin: '10px 20px' 8 | } 9 | 10 | const view = () => { 11 | return ( 12 |
13 |
    14 |
  • Home
  • 15 |
  • Counter
  • 16 |
  • About
  • 17 |
18 |
19 | ); 20 | }; 21 | 22 | export {view}; 23 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/filter/views/filters.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from './link.js'; 3 | import {FilterTypes} from '../../constants.js' 4 | 5 | import './style.css'; 6 | 7 | const Filters = () => { 8 | return ( 9 |

10 | {FilterTypes.ALL} 11 | {FilterTypes.COMPLETED} 12 | {FilterTypes.UNCOMPLETED} 13 |

14 | ); 15 | }; 16 | 17 | export default Filters; 18 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/components/TopMenu/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {Link} from 'react-router'; 4 | 5 | const liStyle = { 6 | display: 'inline-block', 7 | margin: '10px 20px' 8 | } 9 | 10 | const view = () => { 11 | return ( 12 |
13 |
    14 |
  • Home
  • 15 |
  • Counter
  • 16 |
  • About
  • 17 |
18 |
19 | ); 20 | }; 21 | 22 | export {view}; 23 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/server/index.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | require('isomorphic-fetch'); 3 | 4 | const isProductionMode = (process.env.NODE_ENV === 'production'); 5 | const app = isProductionMode ? require('./app.prod.js'): require('./app.dev.js'); 6 | 7 | if (!isProductionMode) { 8 | process.env.NODE_ENV = 'development'; 9 | } 10 | 11 | const PORT = process.env.PORT || 9000; 12 | 13 | app.listen(PORT, function() { 14 | console.log('running in ' + (isProductionMode ? 'producition' : 'development') + ' mode'); 15 | console.log('listening on port: ' + PORT); 16 | }); 17 | -------------------------------------------------------------------------------- /chapter-03/flux/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI 14 | if (!process.env.CI) { 15 | argv.push('--watch'); 16 | } 17 | 18 | 19 | jest.run(argv); 20 | -------------------------------------------------------------------------------- /chapter-03/flux/src/views/ControlPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Counter from './Counter.js'; 3 | import Summary from './Summary.js'; 4 | 5 | const style = { 6 | margin: '20px' 7 | }; 8 | 9 | class ControlPanel extends Component { 10 | 11 | render() { 12 | return ( 13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 | ); 21 | } 22 | } 23 | 24 | export default ControlPanel; 25 | 26 | -------------------------------------------------------------------------------- /chapter-03/react-redux/src/views/ControlPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Counter from './Counter.js'; 3 | import Summary from './Summary.js'; 4 | 5 | const style = { 6 | margin: '20px' 7 | }; 8 | 9 | class ControlPanel extends Component { 10 | render() { 11 | return ( 12 |
13 | 14 | 15 | 16 |
17 | 18 |
19 | ); 20 | } 21 | } 22 | 23 | export default ControlPanel; 24 | 25 | -------------------------------------------------------------------------------- /chapter-03/redux_basic/src/views/ControlPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Counter from './Counter.js'; 3 | import Summary from './Summary.js'; 4 | 5 | const style = { 6 | margin: '20px' 7 | }; 8 | 9 | class ControlPanel extends Component { 10 | render() { 11 | return ( 12 |
13 | 14 | 15 | 16 |
17 | 18 |
19 | ); 20 | } 21 | } 22 | 23 | export default ControlPanel; 24 | 25 | -------------------------------------------------------------------------------- /chapter-06/hoc/src/inheritance/ModifyPropsHOC.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const modifyPropsHOC = (WrappedComponent) => { 4 | return class NewComponent extends WrappedComponent { 5 | render() { 6 | const elements = super.render(); 7 | 8 | const newStyle = { 9 | color: (elements && elements.type === 'div') ? 'red' : 'green' 10 | } 11 | const newProps = {...this.props, style: newStyle}; 12 | 13 | return React.cloneElement(elements, newProps, elements.props.children); 14 | } 15 | }; 16 | }; 17 | 18 | export default modifyPropsHOC; 19 | -------------------------------------------------------------------------------- /chapter-03/redux_smart_dumb/src/views/ControlPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Counter from './Counter.js'; 3 | import Summary from './Summary.js'; 4 | 5 | const style = { 6 | margin: '20px' 7 | }; 8 | 9 | class ControlPanel extends Component { 10 | render() { 11 | return ( 12 |
13 | 14 | 15 | 16 |
17 | 18 |
19 | ); 20 | } 21 | } 22 | 23 | export default ControlPanel; 24 | 25 | -------------------------------------------------------------------------------- /chapter-01/first_react_app/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI 14 | if (!process.env.CI) { 15 | argv.push('--watch'); 16 | } 17 | 18 | 19 | jest.run(argv); 20 | -------------------------------------------------------------------------------- /chapter-02/controlpanel/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI 14 | if (!process.env.CI) { 15 | argv.push('--watch'); 16 | } 17 | 18 | 19 | jest.run(argv); 20 | -------------------------------------------------------------------------------- /chapter-03/react-redux/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI 14 | if (!process.env.CI) { 15 | argv.push('--watch'); 16 | } 17 | 18 | 19 | jest.run(argv); 20 | -------------------------------------------------------------------------------- /chapter-03/redux_basic/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI 14 | if (!process.env.CI) { 15 | argv.push('--watch'); 16 | } 17 | 18 | 19 | jest.run(argv); 20 | -------------------------------------------------------------------------------- /chapter-03/redux_smart_dumb/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI 14 | if (!process.env.CI) { 15 | argv.push('--watch'); 16 | } 17 | 18 | 19 | jest.run(argv); 20 | -------------------------------------------------------------------------------- /chapter-03/redux_with_context/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI 14 | if (!process.env.CI) { 15 | argv.push('--watch'); 16 | } 17 | 18 | 19 | jest.run(argv); 20 | -------------------------------------------------------------------------------- /chapter-03/redux_with_context/src/views/ControlPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Counter from './Counter.js'; 3 | import Summary from './Summary.js'; 4 | 5 | const style = { 6 | margin: '20px' 7 | }; 8 | 9 | class ControlPanel extends Component { 10 | 11 | render() { 12 | return ( 13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 | ); 21 | } 22 | } 23 | 24 | export default ControlPanel; 25 | 26 | -------------------------------------------------------------------------------- /chapter-06/hoc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "chai": "^3.5.0", 7 | "chai-enzyme": "^0.6.1", 8 | "enzyme": "^2.7.0", 9 | "react-addons-test-utils": "^15.4.1", 10 | "react-redux": "^5.0.1", 11 | "react-scripts": "0.8.4", 12 | "redux": "^3.6.0" 13 | }, 14 | "dependencies": { 15 | "react": "^15.4.1", 16 | "react-dom": "^15.4.1", 17 | "react-redux": "^5.0.1", 18 | "redux": "^3.6.0" 19 | }, 20 | "scripts": { 21 | "test": "react-scripts test --env=jsdom" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chapter-11/react_router_basic/src/Store.js: -------------------------------------------------------------------------------- 1 | //import {createStore, compose} from 'redux'; 2 | //const reducer = f => f; 3 | import {createStore, combineReducers, compose} from 'redux'; 4 | import {routerReducer} from 'react-router-redux'; 5 | 6 | const reducer = combineReducers({ 7 | routing: routerReducer 8 | }); 9 | 10 | const win = window; 11 | const storeEnhancers = compose( 12 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 13 | ); 14 | 15 | const initialState = {}; 16 | //const initialState = {}; 17 | export default createStore(reducer, initialState, storeEnhancers); 18 | 19 | -------------------------------------------------------------------------------- /chapter-02/controlpanel_with_summary/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI 14 | if (!process.env.CI) { 15 | argv.push('--watch'); 16 | } 17 | 18 | 19 | jest.run(argv); 20 | -------------------------------------------------------------------------------- /chapter-06/hoc/test/proxy/RefsHOC.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {mount} from 'enzyme'; 3 | 4 | import refsHOC from '../../src/proxy/refsHOC.js'; 5 | 6 | describe('refsHOC', () => { 7 | class DemoComponent extends React.Component { 8 | render() { 9 | return
do something
; 10 | } 11 | } 12 | 13 | it('should get wrapped component by ref', () => { 14 | const NewComponent = refsHOC(DemoComponent); 15 | const wrapper = mount(); 16 | 17 | expect(wrapper.instance()._root).toBeInstanceOf(DemoComponent); 18 | }); 19 | }); 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter-11/react_router_chunked/src/Store.js: -------------------------------------------------------------------------------- 1 | //import {createStore, compose} from 'redux'; 2 | //const reducer = f => f; 3 | import {createStore, combineReducers, compose} from 'redux'; 4 | import {routerReducer} from 'react-router-redux'; 5 | 6 | const reducer = combineReducers({ 7 | routing: routerReducer 8 | }); 9 | 10 | const win = window; 11 | const storeEnhancers = compose( 12 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 13 | ); 14 | 15 | const initialState = {}; 16 | //const initialState = {}; 17 | export default createStore(reducer, initialState, storeEnhancers); 18 | 19 | -------------------------------------------------------------------------------- /chapter-03/react-redux/src/views/Summary.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import {connect} from 'react-redux'; 3 | 4 | function Summary({value}) { 5 | return ( 6 |
Total Count: {value}
7 | ); 8 | } 9 | 10 | Summary.PropTypes = { 11 | value: PropTypes.number.isRequired 12 | }; 13 | 14 | function mapStateToProps(state) { 15 | let sum = 0; 16 | for (const key in state) { 17 | if (state.hasOwnProperty(key)) { 18 | sum += state[key]; 19 | } 20 | } 21 | return {value: sum}; 22 | } 23 | 24 | 25 | export default connect(mapStateToProps)(Summary); 26 | 27 | 28 | -------------------------------------------------------------------------------- /chapter-07/weather_redux/src/weather/reducer.js: -------------------------------------------------------------------------------- 1 | import {FETCH_STARTED, FETCH_SUCCESS, FETCH_FAILURE} from './actionTypes.js'; 2 | import * as Status from './status.js'; 3 | 4 | export default (state = {status: Status.LOADING}, action) => { 5 | switch(action.type) { 6 | case FETCH_STARTED: { 7 | return {status: Status.LOADING}; 8 | } 9 | case FETCH_SUCCESS: { 10 | return {...state, status: Status.SUCCESS, ...action.result}; 11 | } 12 | case FETCH_FAILURE: { 13 | return {status: Status.FAILURE}; 14 | } 15 | default: { 16 | return state; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter-01/first_react_app/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
9 |
10 | logo 11 |

Welcome to React

12 |
13 |

14 | To get started, edit src/App.js and save to reload. 15 |

16 |
17 | ); 18 | } 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /chapter-04/todo/src/filter/views/style.css: -------------------------------------------------------------------------------- 1 | .filters { 2 | font-size: 20px; 3 | } 4 | 5 | .filters .filter { 6 | margin: 3px; 7 | padding: 3px 7px; 8 | text-decoration: none; 9 | border: 1px solid transparent; 10 | border-radius: 3px; 11 | } 12 | 13 | .filters .selected { 14 | border: 1px solid transparent; 15 | text-decoration: none; 16 | } 17 | 18 | .filters .not-selected { 19 | color: black; 20 | text-decoration: none; 21 | border: 1px solid; 22 | border-color: rgba(175, 47, 47, 0.2); 23 | } 24 | 25 | .filters .not-selected:hover { 26 | border-color: rgba(175, 47, 47, 0.2); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /chapter-04/todo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.8.4", 7 | "redux-immutable-state-invariant": "^1.2.4" 8 | }, 9 | "dependencies": { 10 | "react": "^15.4.1", 11 | "react-addons-perf": "^15.4.1", 12 | "react-dom": "^15.4.1", 13 | "react-redux": "^5.0.1", 14 | "redux": "^3.6.0" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test --env=jsdom", 20 | "eject": "react-scripts eject" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/filter/views/style.css: -------------------------------------------------------------------------------- 1 | .filters { 2 | font-size: 20px; 3 | } 4 | 5 | .filters .filter { 6 | margin: 3px; 7 | padding: 3px 7px; 8 | text-decoration: none; 9 | border: 1px solid transparent; 10 | border-radius: 3px; 11 | } 12 | 13 | .filters .selected { 14 | border: 1px solid transparent; 15 | text-decoration: none; 16 | } 17 | 18 | .filters .not-selected { 19 | color: black; 20 | text-decoration: none; 21 | border: 1px solid; 22 | border-color: rgba(175, 47, 47, 0.2); 23 | } 24 | 25 | .filters .not-selected:hover { 26 | border-color: rgba(175, 47, 47, 0.2); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /chapter-07/weather_redux_improved/src/weather/reducer.js: -------------------------------------------------------------------------------- 1 | import {FETCH_STARTED, FETCH_SUCCESS, FETCH_FAILURE} from './actionTypes.js'; 2 | import * as Status from './status.js'; 3 | 4 | export default (state = {status: Status.LOADING}, action) => { 5 | switch(action.type) { 6 | case FETCH_STARTED: { 7 | return {status: Status.LOADING}; 8 | } 9 | case FETCH_SUCCESS: { 10 | return {...state, status: Status.SUCCESS, ...action.result}; 11 | } 12 | case FETCH_FAILURE: { 13 | return {status: Status.FAILURE}; 14 | } 15 | default: { 16 | return state; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/filter/views/style.css: -------------------------------------------------------------------------------- 1 | .filters { 2 | font-size: 20px; 3 | } 4 | 5 | .filters .filter { 6 | margin: 3px; 7 | padding: 3px 7px; 8 | text-decoration: none; 9 | border: 1px solid transparent; 10 | border-radius: 3px; 11 | } 12 | 13 | .filters .selected { 14 | border: 1px solid transparent; 15 | text-decoration: none; 16 | } 17 | 18 | .filters .not-selected { 19 | color: black; 20 | text-decoration: none; 21 | border: 1px solid; 22 | border-color: rgba(175, 47, 47, 0.2); 23 | } 24 | 25 | .filters .not-selected:hover { 26 | border-color: rgba(175, 47, 47, 0.2); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /chapter-11/react_router_basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multi_pages", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.8.4" 7 | }, 8 | "dependencies": { 9 | "react": "^15.4.1", 10 | "react-dom": "^15.4.1", 11 | "react-redux": "^5.0.1", 12 | "react-router": "^3.0.0", 13 | "react-router-redux": "^4.0.7", 14 | "redux": "^3.6.0" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test --env=jsdom", 20 | "eject": "react-scripts eject" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.8.4", 7 | "redux-immutable-state-invariant": "^1.2.4" 8 | }, 9 | "dependencies": { 10 | "react": "^15.4.1", 11 | "react-addons-perf": "^15.4.1", 12 | "react-dom": "^15.4.1", 13 | "react-redux": "^5.0.1", 14 | "redux": "^3.6.0" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test --env=jsdom", 20 | "eject": "react-scripts eject" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/filter/views/style.css: -------------------------------------------------------------------------------- 1 | .filters { 2 | font-size: 20px; 3 | } 4 | 5 | .filters .filter { 6 | margin: 3px; 7 | padding: 3px 7px; 8 | text-decoration: none; 9 | border: 1px solid transparent; 10 | border-radius: 3px; 11 | } 12 | 13 | .filters .selected { 14 | border: 1px solid transparent; 15 | text-decoration: none; 16 | } 17 | 18 | .filters .not-selected { 19 | color: black; 20 | text-decoration: none; 21 | border: 1px solid; 22 | border-color: rgba(175, 47, 47, 0.2); 23 | } 24 | 25 | .filters .not-selected:hover { 26 | border-color: rgba(175, 47, 47, 0.2); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/filter/views/style.css: -------------------------------------------------------------------------------- 1 | .filters { 2 | font-size: 20px; 3 | } 4 | 5 | .filters .filter { 6 | margin: 3px; 7 | padding: 3px 7px; 8 | text-decoration: none; 9 | border: 1px solid transparent; 10 | border-radius: 3px; 11 | } 12 | 13 | .filters .selected { 14 | border: 1px solid transparent; 15 | text-decoration: none; 16 | } 17 | 18 | .filters .not-selected { 19 | color: black; 20 | text-decoration: none; 21 | border: 1px solid; 22 | border-color: rgba(175, 47, 47, 0.2); 23 | } 24 | 25 | .filters .not-selected:hover { 26 | border-color: rgba(175, 47, 47, 0.2); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /chapter-12/express_server/server/app.prod.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | 4 | const app = express(); 5 | 6 | const assetManifest = require(path.resolve(__dirname, '../build/asset-manifest.json')); 7 | 8 | app.use(express.static(path.resolve(__dirname, '../build'))); 9 | 10 | app.get('*', (req, res) => { 11 | return res.render('index', { 12 | title: 'Sample React App', 13 | PUBLIC_URL: '/', 14 | assetManifest: assetManifest 15 | }); 16 | }); 17 | 18 | app.set('view engine', 'ejs'); 19 | app.set('views', path.resolve(__dirname, 'views')); 20 | 21 | module.exports = app; 22 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/filter/views/style.css: -------------------------------------------------------------------------------- 1 | .filters { 2 | font-size: 20px; 3 | } 4 | 5 | .filters .filter { 6 | margin: 3px; 7 | padding: 3px 7px; 8 | text-decoration: none; 9 | border: 1px solid transparent; 10 | border-radius: 3px; 11 | } 12 | 13 | .filters .selected { 14 | border: 1px solid transparent; 15 | text-decoration: none; 16 | } 17 | 18 | .filters .not-selected { 19 | color: black; 20 | text-decoration: none; 21 | border: 1px solid; 22 | border-color: rgba(175, 47, 47, 0.2); 23 | } 24 | 25 | .filters .not-selected:hover { 26 | border-color: rgba(175, 47, 47, 0.2); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/src/weather/actions.js: -------------------------------------------------------------------------------- 1 | import {FETCH_STARTED, FETCH_SUCCESS, FETCH_FAILURE} from './actionTypes.js'; 2 | 3 | export const fetchWeather = (cityCode) => { 4 | const apiUrl = `/data/cityinfo/${cityCode}.html`; 5 | 6 | return { 7 | promise: fetch(apiUrl).then(response => { 8 | if (response.status !== 200) { 9 | throw new Error('Fail to get response with status ' + response.status); 10 | } 11 | 12 | return response.json().then(responseJson => responseJson.weatherinfo); 13 | }), 14 | types: [FETCH_STARTED, FETCH_SUCCESS, FETCH_FAILURE] 15 | }; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.8.4", 7 | "redux-immutable-state-invariant": "^1.2.4" 8 | }, 9 | "dependencies": { 10 | "react": "^15.4.1", 11 | "react-addons-perf": "^15.4.1", 12 | "react-dom": "^15.4.1", 13 | "react-redux": "^5.0.1", 14 | "redux": "^3.6.0" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test --env=jsdom", 20 | "eject": "react-scripts eject" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chapter-06/function_as_child/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "chai": "^3.5.0", 7 | "chai-enzyme": "^0.6.1", 8 | "enzyme": "^2.7.0", 9 | "react-addons-test-utils": "^15.4.1", 10 | "react-redux": "^5.0.1", 11 | "react-scripts": "0.8.4", 12 | "redux": "^3.6.0" 13 | }, 14 | "dependencies": { 15 | "react": "^15.4.1", 16 | "react-dom": "^15.4.1", 17 | "react-redux": "^5.0.1", 18 | "redux": "^3.6.0", 19 | "sinon": "^1.17.6" 20 | }, 21 | "scripts": { 22 | "test": "react-scripts test --env=jsdom" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/src/weather/reducer.js: -------------------------------------------------------------------------------- 1 | import {FETCH_STARTED, FETCH_SUCCESS, FETCH_FAILURE} from './actionTypes.js'; 2 | import * as Status from './status.js'; 3 | 4 | export default (state = {status: Status.LOADING}, action) => { 5 | switch(action.type) { 6 | case FETCH_STARTED: { 7 | return {status: Status.LOADING}; 8 | } 9 | case FETCH_SUCCESS: { 10 | return {...state, status: Status.SUCCESS, ...action.result}; 11 | } 12 | case FETCH_FAILURE: { 13 | return {status: Status.FAILURE, error: action.error}; 14 | } 15 | default: { 16 | return state; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/server/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= title %> 8 | 9 | 10 |
<%- appHtml %>
11 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/server/app.prod.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | const renderPage = require('./routes.Server.js').renderPage; 4 | 5 | const app = express(); 6 | 7 | const assetManifest = require(path.resolve(__dirname, '../build/asset-manifest.json')); 8 | 9 | app.use(express.static(path.resolve(__dirname, '../build'))); 10 | 11 | app.use('/api/count', (req, res) => { 12 | res.json({count: 100}); 13 | }); 14 | 15 | app.get('*', (req, res) => { 16 | return renderPage(req, res, assetManifest); 17 | }); 18 | 19 | app.set('view engine', 'ejs'); 20 | app.set('views', path.resolve(__dirname, 'views')); 21 | 22 | module.exports = app; 23 | -------------------------------------------------------------------------------- /chapter-02/controlpanel/src/ControlPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Counter from './Counter.js'; 3 | 4 | const style = { 5 | margin: '20px' 6 | }; 7 | 8 | class ControlPanel extends Component { 9 | render() { 10 | console.log('enter ControlPanel render'); 11 | return ( 12 |
13 | 14 | 15 | 16 | 19 |
20 | ); 21 | } 22 | } 23 | 24 | export default ControlPanel; 25 | 26 | -------------------------------------------------------------------------------- /chapter-03/flux/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /chapter-09/promise_middleware/src/advanced.js: -------------------------------------------------------------------------------- 1 | function isPromise(obj) { 2 | return obj && typeof obj.then === 'function'; 3 | } 4 | 5 | export default function promiseMiddleware({dispatch}) { 6 | return (next) => (action) => { 7 | const {types, promise, ...rest} = action; 8 | if (!isPromise(promise) || !(action.types && action.types.length === 3)) { 9 | return next(action); 10 | } 11 | 12 | const [PENDING, DONE, FAIL] = types; 13 | 14 | dispatch({...rest, type: PENDING}); 15 | return action.promise.then( 16 | (result) => dispatch({...rest, result, type: DONE}), 17 | (error) => dispatch({...rest, error, type: FAIL}) 18 | ); 19 | }; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /chapter-02/controlpanel/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /chapter-03/react-redux/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /chapter-03/redux_basic/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /chapter-01/first_react_app/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /chapter-03/redux_smart_dumb/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /chapter-03/redux_with_context/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /chapter-12/express_server/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /chapter-11/react_router_chunked/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /chapter-02/controlpanel_with_summary/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /chapter-06/function_as_child/test/countDown.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {mount} from 'enzyme'; 3 | import {spy} from 'sinon'; 4 | 5 | import CountDown from '../src/CountDown.js'; 6 | 7 | describe('CountDown', () => { 8 | it('should count down', (done) => { 9 | const counter = spy(); 10 | const wrapper = mount( 11 | 12 | { 13 | (count) => { 14 | counter(); 15 | if (count === 0) { 16 | expect(counter.callCount).toEqual(3); 17 | done(); 18 | } 19 | return null; 20 | } 21 | } 22 | 23 | ); 24 | }); 25 | 26 | }); 27 | 28 | 29 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.8.4", 7 | "redux-immutable-state-invariant": "^1.2.4" 8 | }, 9 | "dependencies": { 10 | "react": "^15.4.1", 11 | "react-addons-css-transition-group": "^15.4.1", 12 | "react-addons-perf": "^15.4.1", 13 | "react-dom": "^15.4.1", 14 | "react-redux": "^5.0.1", 15 | "redux": "^3.6.0", 16 | "reselect": "^2.5.4" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test --env=jsdom", 22 | "eject": "react-scripts eject" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter-07/weather_redux_improved/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weather", 3 | "version": "0.1.0", 4 | "private": true, 5 | "proxy": "http://www.weather.com.cn/", 6 | "devDependencies": { 7 | "react-scripts": "0.8.4", 8 | "redux-immutable-state-invariant": "^1.2.4" 9 | }, 10 | "dependencies": { 11 | "react": "^15.4.1", 12 | "react-addons-perf": "^15.4.1", 13 | "react-dom": "^15.4.1", 14 | "react-redux": "^5.0.1", 15 | "redux": "^3.6.0", 16 | "redux-thunk": "^2.1.0" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test --env=jsdom", 22 | "eject": "react-scripts eject" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter-10/animation_types/setInterval_animation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 |
15 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/todos/selector.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | import {FilterTypes} from '../constants.js'; 3 | 4 | const getFilter = (state) => state.filter; 5 | const getTodos = (state) => state.todos; 6 | 7 | export const selectVisibleTodos = createSelector( 8 | [getFilter, getTodos], 9 | (filter, todos) => { 10 | switch (filter) { 11 | case FilterTypes.ALL: 12 | return todos; 13 | case FilterTypes.COMPLETED: 14 | return todos.filter(item => item.completed); 15 | case FilterTypes.UNCOMPLETED: 16 | return todos.filter(item => !item.completed); 17 | default: 18 | throw new Error('unsupported filter'); 19 | } 20 | } 21 | ); 22 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/todos/selector.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | import {FilterTypes} from '../constants.js'; 3 | 4 | const getFilter = (state) => state.filter; 5 | const getTodos = (state) => state.todos; 6 | 7 | export const selectVisibleTodos = createSelector( 8 | [getFilter, getTodos], 9 | (filter, todos) => { 10 | switch (filter) { 11 | case FilterTypes.ALL: 12 | return todos; 13 | case FilterTypes.COMPLETED: 14 | return todos.filter(item => item.completed); 15 | case FilterTypes.UNCOMPLETED: 16 | return todos.filter(item => !item.completed); 17 | default: 18 | throw new Error('unsupported filter'); 19 | } 20 | } 21 | ); 22 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/todos/selector.js: -------------------------------------------------------------------------------- 1 | import {createSelector} from 'reselect'; 2 | import {FilterTypes} from '../constants.js'; 3 | 4 | const getFilter = (state) => state.filter; 5 | const getTodos = (state) => state.todos; 6 | 7 | export const selectVisibleTodos = createSelector( 8 | [getFilter, getTodos], 9 | (filter, todos) => { 10 | switch (filter) { 11 | case FilterTypes.ALL: 12 | return todos; 13 | case FilterTypes.COMPLETED: 14 | return todos.filter(item => item.completed); 15 | case FilterTypes.UNCOMPLETED: 16 | return todos.filter(item => !item.completed); 17 | default: 18 | throw new Error('unsupported filter'); 19 | } 20 | } 21 | ); 22 | -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weather", 3 | "version": "0.1.0", 4 | "private": true, 5 | "proxy": "http://www.weather.com.cn/", 6 | "devDependencies": { 7 | "react-scripts": "0.8.4", 8 | "redux-immutable-state-invariant": "^1.2.4" 9 | }, 10 | "dependencies": { 11 | "react": "^15.4.1", 12 | "react-addons-perf": "^15.4.1", 13 | "react-dom": "^15.4.1", 14 | "react-redux": "^5.0.1", 15 | "redux": "^3.6.0", 16 | "redux-thunk": "^2.1.0" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test --env=jsdom", 22 | "eject": "react-scripts eject" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/src/middleware/promise_middleware.js: -------------------------------------------------------------------------------- 1 | function isPromise(obj) { 2 | return obj && typeof obj.then === 'function'; 3 | } 4 | 5 | export default function promiseMiddleware({dispatch}) { 6 | return (next) => (action) => { 7 | const {types, promise, ...rest} = action; 8 | if (!isPromise(promise) || !(action.types && action.types.length === 3)) { 9 | return next(action); 10 | } 11 | 12 | const [PENDING, DONE, FAIL] = types; 13 | 14 | dispatch({...rest, type: PENDING}); 15 | return action.promise.then( 16 | (result) => dispatch({...rest, result, type: DONE}), 17 | (error) => dispatch({...rest, error, type: FAIL}) 18 | ); 19 | }; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /chapter-06/hoc/test/proxy/removeUserPropHOC.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {mount, shallow} from 'enzyme'; 3 | 4 | import removeUserPropHOC from '../../src/proxy/removeUserPropHOC.js'; 5 | 6 | describe('removeUserPropHOC', () => { 7 | 8 | const DemoComponent = (props) => { 9 | return ( 10 |
render something.
11 | ); 12 | }; 13 | 14 | it('should pass new props to wrapped component', () => { 15 | const NewComponent = removeUserPropHOC(DemoComponent); 16 | const wrapper = mount(); 17 | const expectedComponent = 18 | 19 | expect(wrapper.contains(expectedComponent)).toEqual(true); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "enzyme": "^2.7.0", 7 | "react-addons-test-utils": "^15.4.2", 8 | "react-scripts": "0.8.4", 9 | "redux-immutable-state-invariant": "^1.2.4" 10 | }, 11 | "dependencies": { 12 | "react": "^15.4.1", 13 | "react-addons-perf": "^15.4.1", 14 | "react-dom": "^15.4.1", 15 | "react-redux": "^5.0.1", 16 | "redux": "^3.6.0", 17 | "reselect": "^2.5.4" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test --env=jsdom", 23 | "eject": "react-scripts eject" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/pages/CounterPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {view as Counter, stateKey, reducer} from '../components/Counter'; 3 | 4 | const page = () => { 5 | return ( 6 |
7 |
Counter
8 | 9 |
10 | ); 11 | }; 12 | 13 | const END_POINT = process.env.HOST_NAME || 'localhost:9000'; 14 | 15 | const initState = () => { 16 | return fetch(`http://${END_POINT}/api/count`).then(response => { 17 | if (response.status !== 200) { 18 | throw new Error('Fail to fetch count'); 19 | } 20 | return response.json(); 21 | }).then(responseJson => { 22 | return responseJson.count; 23 | }); 24 | } 25 | 26 | export {page, reducer, initState, stateKey}; 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-and-redux 2 | 3 | 《深入浅出React和Redux》已近由机械工业出版社发行,这个repo存放的是书中所有的代码。 4 | 5 | 配合React和Redux的持续讨论,作者开通了一个知乎专栏[《进击的React》](https://zhuanlan.zhihu.com/advancing-react),欢迎关注。对React和Redux技术有问题可以[通过私信或者值乎咨询](https://www.zhihu.com/zhi/people/828707098316656640),有问必答。 6 | 7 | [京东](http://item.jd.com/12073933.html)(这个链接里有书的目录) 8 | 9 | [亚马逊](https://www.amazon.cn/%E6%B7%B1%E5%85%A5%E6%B5%85%E5%87%BAReact%E5%92%8CRedux-%E7%A8%8B%E5%A2%A8/dp/B072BM636Z/ref=sr_1_1?ie=UTF8&qid=1494646329&sr=8-1&keywords=%E6%B7%B1%E5%85%A5%E6%B5%85%E5%87%BAreact%E5%92%8Credux) 10 | 11 | [当当网](http://product.dangdang.com/25072226.html) 12 | 13 | ![cover_high](https://cloud.githubusercontent.com/assets/239291/25560742/c3199d9a-2d8e-11e7-81a9-4e11c518e512.jpg) 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "enzyme": "^2.7.0", 7 | "react-addons-test-utils": "^15.4.2", 8 | "react-scripts": "0.8.4", 9 | "redux-immutable-state-invariant": "^1.2.4" 10 | }, 11 | "dependencies": { 12 | "react": "^15.4.1", 13 | "react-addons-perf": "^15.4.1", 14 | "react-dom": "^15.4.1", 15 | "react-motion": "^0.4.7", 16 | "react-redux": "^5.0.1", 17 | "redux": "^3.6.0", 18 | "reselect": "^2.5.4" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test --env=jsdom", 24 | "eject": "react-scripts eject" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | build 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | 40 | .DS_Store 41 | -------------------------------------------------------------------------------- /chapter-07/weather_redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weather", 3 | "version": "0.1.0", 4 | "private": true, 5 | "proxy": "http://www.weather.com.cn/", 6 | "devDependencies": { 7 | "react-scripts": "0.8.4", 8 | "redux-immutable-state-invariant": "^1.2.4", 9 | "redux-mock-store": "^1.2.1", 10 | "sinon": "^1.17.7" 11 | }, 12 | "dependencies": { 13 | "react": "^15.4.1", 14 | "react-addons-perf": "^15.4.1", 15 | "react-dom": "^15.4.1", 16 | "react-redux": "^5.0.1", 17 | "redux": "^3.6.0", 18 | "redux-thunk": "^2.1.0" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test --env=jsdom", 24 | "eject": "react-scripts eject" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/test/filter/views/filters.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {shallow} from 'enzyme'; 3 | import Filters from '../../../src/filter/views/filters.js'; 4 | import Link from '../../../src/filter/views/link.js'; 5 | import {FilterTypes} from '../../../src/constants.js'; 6 | 7 | describe('filters', () => { 8 | it('should render three link', () => { 9 | const wrapper = shallow(); 10 | 11 | expect(wrapper.contains( {FilterTypes.ALL} )).toBe(true); 12 | expect(wrapper.contains( {FilterTypes.COMPLETED} )).toBe(true); 13 | expect(wrapper.contains( {FilterTypes.UNCOMPLETED} )).toBe(true); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /chapter-06/hoc/src/inheritance/removeUserPropHOC.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function removeUserProp(WrappedComponent) { 4 | return class NewComponent extends WrappedComponent { 5 | render() { 6 | const {user, ...otherProps} = this.props; 7 | this.props = otherProps; 8 | return super.render(); 9 | } 10 | }; 11 | } 12 | 13 | /* 14 | function removeUserProp(WrappedComponent) { 15 | return class NewComponent extends WrappedComponent { 16 | render() { 17 | const elements = super.render(); 18 | const {user, ...otherProps} = this.props; 19 | 20 | console.log('##', elements); 21 | 22 | return React.cloneElement(elements, otherProps, elements.props.children); 23 | } 24 | }; 25 | } 26 | */ 27 | 28 | export default removeUserProp; 29 | -------------------------------------------------------------------------------- /chapter-06/hoc/test/inheritance/removeUserPropHOC.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {mount, shallow} from 'enzyme'; 3 | 4 | import removeUserPropHOC from '../../src/inheritance/removeUserPropHOC.js'; 5 | 6 | describe('removeUserPropHOC', () => { 7 | 8 | class DemoComponent extends React.Component { 9 | render() { 10 | return ( 11 |
{this.props.user || 'no_user'}
12 | ); 13 | } 14 | } 15 | 16 | it('should pass new props to wrapped component', () => { 17 | const NewComponent = removeUserPropHOC(DemoComponent); 18 | const wrapper = mount(); 19 | const expectedComponent =
no_user
; 20 | 21 | expect(wrapper.contains(expectedComponent)).toEqual(true); 22 | }); 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /chapter-04/todo/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'; 2 | 3 | import {reducer as todoReducer} from './todos'; 4 | import {reducer as filterReducer} from './filter'; 5 | 6 | import Perf from 'react-addons-perf' 7 | 8 | const win = window; 9 | win.Perf = Perf 10 | 11 | const reducer = combineReducers({ 12 | todos: todoReducer, 13 | filter: filterReducer 14 | }); 15 | 16 | const middlewares = []; 17 | if (process.env.NODE_ENV !== 'production') { 18 | middlewares.push(require('redux-immutable-state-invariant')()); 19 | } 20 | 21 | const storeEnhancers = compose( 22 | applyMiddleware(...middlewares), 23 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 24 | ); 25 | 26 | export default createStore(reducer, {}, storeEnhancers); 27 | -------------------------------------------------------------------------------- /chapter-09/reset_enhancer/src/reset.js: -------------------------------------------------------------------------------- 1 | const RESET_ACTION_TYPE = '@@RESET'; 2 | 3 | const resetReducerCreator = (reducer, resetState) => (state, action) => { 4 | if (action.type === RESET_ACTION_TYPE) { 5 | return resetState; 6 | } else { 7 | return reducer(state, action); 8 | } 9 | }; 10 | 11 | const reset = (createStore) => (reducer, preloadedState, enhancer) => { 12 | const store = createStore(reducer, preloadedState, enhancer); 13 | 14 | const reset = (resetReducer, resetState) => { 15 | const newReducer = resetReducerCreator(resetReducer, resetState); 16 | store.replaceReducer(newReducer); 17 | store.dispatch({type: RESET_ACTION_TYPE, state: resetState}); 18 | }; 19 | 20 | return { 21 | ...store, 22 | reset 23 | }; 24 | }; 25 | 26 | export default reset; 27 | 28 | -------------------------------------------------------------------------------- /chapter-07/weather_redux/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'; 2 | 3 | import thunkMiddleware from 'redux-thunk' 4 | 5 | import {reducer as weatherReducer} from './weather/'; 6 | 7 | import Perf from 'react-addons-perf' 8 | 9 | const win = window; 10 | win.Perf = Perf 11 | 12 | const reducer = combineReducers({ 13 | weather: weatherReducer 14 | }); 15 | 16 | const middlewares = [thunkMiddleware]; 17 | if (process.env.NODE_ENV !== 'production') { 18 | middlewares.push(require('redux-immutable-state-invariant')()); 19 | } 20 | 21 | const storeEnhancers = compose( 22 | applyMiddleware(...middlewares), 23 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 24 | ); 25 | 26 | export default createStore(reducer, {}, storeEnhancers); 27 | 28 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/enhancer/reset.js: -------------------------------------------------------------------------------- 1 | const RESET_ACTION_TYPE = '@@RESET'; 2 | 3 | const resetReducerCreator = (reducer, resetState) => (state, action) => { 4 | if (action.type === RESET_ACTION_TYPE) { 5 | return resetState; 6 | } else { 7 | return reducer(state, action); 8 | } 9 | }; 10 | 11 | const reset = (createStore) => (reducer, preloadedState, enhancer) => { 12 | const store = createStore(reducer, preloadedState, enhancer); 13 | 14 | const reset = (resetReducer, resetState) => { 15 | const newReducer = resetReducerCreator(resetReducer, resetState); 16 | store.replaceReducer(newReducer); 17 | store.dispatch({type: RESET_ACTION_TYPE, state: resetState}); 18 | }; 19 | 20 | return { 21 | ...store, 22 | reset 23 | }; 24 | }; 25 | 26 | export default reset; 27 | 28 | -------------------------------------------------------------------------------- /chapter-01/first_react_app/src/ClickCounter.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class ClickCounter extends Component { 4 | 5 | constructor(props) { 6 | super(props); 7 | this.onClickButton = this.onClickButton.bind(this); 8 | this.state = { 9 | count: 0 10 | } 11 | } 12 | 13 | onClickButton() { 14 | this.setState({count: this.state.count + 1}); 15 | } 16 | 17 | render() { 18 | const counterStyle = { 19 | margin: '16px' 20 | } 21 | return ( 22 |
23 | 24 |
25 | Click Count: {this.state.count} 26 |
27 |
28 | ); 29 | } 30 | } 31 | 32 | export default ClickCounter; 33 | 34 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/enhancer/reset.js: -------------------------------------------------------------------------------- 1 | const RESET_ACTION_TYPE = '@@RESET'; 2 | 3 | const resetReducerCreator = (reducer, resetState) => (state, action) => { 4 | if (action.type === RESET_ACTION_TYPE) { 5 | return resetState; 6 | } else { 7 | return reducer(state, action); 8 | } 9 | }; 10 | 11 | const reset = (createStore) => (reducer, preloadedState, enhancer) => { 12 | const store = createStore(reducer, preloadedState, enhancer); 13 | 14 | const reset = (resetReducer, resetState) => { 15 | const newReducer = resetReducerCreator(resetReducer, resetState); 16 | store.replaceReducer(newReducer); 17 | store.dispatch({type: RESET_ACTION_TYPE, state: resetState}); 18 | }; 19 | 20 | return { 21 | ...store, 22 | reset 23 | }; 24 | }; 25 | 26 | export default reset; 27 | 28 | -------------------------------------------------------------------------------- /chapter-07/weather_redux_improved/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'; 2 | 3 | import thunkMiddleware from 'redux-thunk' 4 | 5 | import {reducer as weatherReducer} from './weather/'; 6 | 7 | import Perf from 'react-addons-perf' 8 | 9 | const win = window; 10 | win.Perf = Perf 11 | 12 | const reducer = combineReducers({ 13 | weather: weatherReducer 14 | }); 15 | 16 | const middlewares = [thunkMiddleware]; 17 | if (process.env.NODE_ENV !== 'production') { 18 | middlewares.push(require('redux-immutable-state-invariant')()); 19 | } 20 | 21 | const storeEnhancers = compose( 22 | applyMiddleware(...middlewares), 23 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 24 | ); 25 | 26 | export default createStore(reducer, {}, storeEnhancers); 27 | 28 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/enhancer/reset.js: -------------------------------------------------------------------------------- 1 | const RESET_ACTION_TYPE = '@@RESET'; 2 | 3 | const resetReducerCreator = (reducer, resetState) => (state, action) => { 4 | if (action.type === RESET_ACTION_TYPE) { 5 | return resetState; 6 | } else { 7 | return reducer(state, action); 8 | } 9 | }; 10 | 11 | const reset = (createStore) => (reducer, preloadedState, enhancer) => { 12 | const store = createStore(reducer, preloadedState, enhancer); 13 | 14 | const reset = (resetReducer, resetState) => { 15 | const newReducer = resetReducerCreator(resetReducer, resetState); 16 | store.replaceReducer(newReducer); 17 | store.dispatch({type: RESET_ACTION_TYPE, state: resetState}); 18 | }; 19 | 20 | return { 21 | ...store, 22 | reset 23 | }; 24 | }; 25 | 26 | export default reset; 27 | 28 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'; 2 | 3 | import {reducer as todoReducer} from './todos'; 4 | import {reducer as filterReducer} from './filter'; 5 | 6 | import Perf from 'react-addons-perf' 7 | 8 | const win = window; 9 | win.Perf = Perf 10 | 11 | const reducer = combineReducers({ 12 | todos: todoReducer, 13 | filter: filterReducer 14 | }); 15 | 16 | const middlewares = []; 17 | if (process.env.NODE_ENV !== 'production') { 18 | middlewares.push(require('redux-immutable-state-invariant')()); 19 | } 20 | 21 | const storeEnhancers = compose( 22 | applyMiddleware(...middlewares), 23 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 24 | ); 25 | 26 | export default createStore(reducer, {}, storeEnhancers); 27 | -------------------------------------------------------------------------------- /chapter-03/flux/src/views/Summary.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import SummaryStore from '../stores/SummaryStore.js'; 4 | 5 | class Summary extends Component { 6 | 7 | constructor(props) { 8 | super(props); 9 | 10 | this.onUpdate = this.onUpdate.bind(this); 11 | 12 | this.state = { 13 | sum: SummaryStore.getSummary() 14 | } 15 | } 16 | 17 | componentDidMount() { 18 | SummaryStore.addChangeListener(this.onUpdate); 19 | } 20 | 21 | componentWillUnmount() { 22 | SummaryStore.removeChangeListener(this.onUpdate); 23 | } 24 | 25 | onUpdate() { 26 | this.setState({ 27 | sum: SummaryStore.getSummary() 28 | }) 29 | } 30 | 31 | render() { 32 | return ( 33 |
Total Count: {this.state.sum}
34 | ); 35 | } 36 | } 37 | 38 | export default Summary; 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/todos/views/todoList.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import TodoItem from './todoItem.js'; 4 | import {selectVisibleTodos} from '../selector.js'; 5 | 6 | const TodoList = ({todos, onClickTodo}) => { 7 | return ( 8 |
    9 | { 10 | todos.map((item) => ( 11 | 17 | )) 18 | } 19 |
20 | ); 21 | }; 22 | 23 | TodoList.propTypes = { 24 | todos: PropTypes.array.isRequired 25 | }; 26 | 27 | const mapStateToProps = (state) => { 28 | return { 29 | todos: selectVisibleTodos(state) 30 | }; 31 | } 32 | 33 | export default connect(mapStateToProps)(TodoList); 34 | -------------------------------------------------------------------------------- /chapter-04/todo/src/todos/views/todoItem.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | const TodoItem = ({onToggle, onRemove, completed, text}) => { 4 | const checkedProp = completed ? {checked: true} : {}; 5 | return ( 6 |
  • 12 | 13 | 14 | 15 |
  • 16 | ) 17 | } 18 | 19 | 20 | TodoItem.propTypes = { 21 | onToggle: PropTypes.func.isRequired, 22 | onRemove: PropTypes.func.isRequired, 23 | completed: PropTypes.bool.isRequired, 24 | text: PropTypes.string.isRequired 25 | } 26 | 27 | export default TodoItem; 28 | -------------------------------------------------------------------------------- /chapter-06/hoc/test/proxy/StyleHOC.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {mount} from 'enzyme'; 3 | 4 | import chai from 'chai'; 5 | import chaiEnzyme from 'chai-enzyme'; 6 | chai.use(chaiEnzyme()); 7 | 8 | const {expect} = chai; 9 | 10 | 11 | import styleHOC from '../../src/proxy/styleHOC.js'; 12 | 13 | describe('styleHOC', () => { 14 | 15 | class DemoComponent extends React.Component { 16 | render() { 17 | return do something; 18 | } 19 | } 20 | 21 | it('should get right style', () => { 22 | const NewComponent = styleHOC(DemoComponent, {color: 'red'}); 23 | 24 | const wrapper = mount(); 25 | 26 | expect(wrapper.find('div')).to.have.style('color').equal('red'); 27 | expect(wrapper.find('span')).to.have.style('color').equal('green'); 28 | }); 29 | }); 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /chapter-04/todo/src/todos/reducer.js: -------------------------------------------------------------------------------- 1 | import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO}from './actionTypes.js'; 2 | 3 | export default (state = [], action) => { 4 | switch(action.type) { 5 | case ADD_TODO: { 6 | return [ 7 | { 8 | id: action.id, 9 | text: action.text, 10 | completed: false 11 | }, 12 | ...state 13 | ] 14 | } 15 | case TOGGLE_TODO: { 16 | return state.map((todoItem) => { 17 | if (todoItem.id === action.id) { 18 | return {...todoItem, completed: !todoItem.completed}; 19 | } else { 20 | return todoItem; 21 | } 22 | }) 23 | } 24 | case REMOVE_TODO: { 25 | return state.filter((todoItem) => { 26 | return todoItem.id !== action.id; 27 | }) 28 | } 29 | default: { 30 | return state; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/todos/reducer.js: -------------------------------------------------------------------------------- 1 | import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO}from './actionTypes.js'; 2 | 3 | export default (state = [], action) => { 4 | switch(action.type) { 5 | case ADD_TODO: { 6 | return [ 7 | { 8 | id: action.id, 9 | text: action.text, 10 | completed: false 11 | }, 12 | ...state 13 | ] 14 | } 15 | case TOGGLE_TODO: { 16 | return state.map((todoItem) => { 17 | if (todoItem.id === action.id) { 18 | return {...todoItem, completed: !todoItem.completed}; 19 | } else { 20 | return todoItem; 21 | } 22 | }) 23 | } 24 | case REMOVE_TODO: { 25 | return state.filter((todoItem) => { 26 | return todoItem.id !== action.id; 27 | }) 28 | } 29 | default: { 30 | return state; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/test/todos/actions.test.js: -------------------------------------------------------------------------------- 1 | import * as actions from '../../src/todos/actions.js'; 2 | import * as actionTypes from '../../src/todos/actionTypes.js'; 3 | 4 | describe('todos/actions', () => { 5 | describe('addTodo', () => { 6 | const addTodo = actions.addTodo 7 | 8 | it('should create an action to add todo', () => { 9 | const text = 'first todo'; 10 | const action = addTodo(text); 11 | 12 | expect(action.text).toBe(text); 13 | expect(action.completed).toBe(false); 14 | expect(action.type).toBe(actionTypes.ADD_TODO); 15 | }); 16 | 17 | it('should have different id for different actions', () => { 18 | const text = 'first todo'; 19 | const action1 = addTodo(text); 20 | const action2 = addTodo(text); 21 | 22 | expect(action1.id !== action2.id).toBe(true); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/todos/reducer.js: -------------------------------------------------------------------------------- 1 | import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO}from './actionTypes.js'; 2 | 3 | export default (state = [], action) => { 4 | switch(action.type) { 5 | case ADD_TODO: { 6 | return [ 7 | { 8 | id: action.id, 9 | text: action.text, 10 | completed: false 11 | }, 12 | ...state 13 | ] 14 | } 15 | case TOGGLE_TODO: { 16 | return state.map((todoItem) => { 17 | if (todoItem.id === action.id) { 18 | return {...todoItem, completed: !todoItem.completed}; 19 | } else { 20 | return todoItem; 21 | } 22 | }) 23 | } 24 | case REMOVE_TODO: { 25 | return state.filter((todoItem) => { 26 | return todoItem.id !== action.id; 27 | }) 28 | } 29 | default: { 30 | return state; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/todos/views/todoItem.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | 3 | const TodoItem = ({onToggle, onRemove, completed, text }) => { 4 | const checkedProp = completed ? {checked: true} : {}; 5 | return ( 6 |
  • 12 | 13 | 14 | 15 |
  • 16 | ); 17 | } 18 | 19 | 20 | TodoItem.propTypes = { 21 | onToggle: PropTypes.func.isRequired, 22 | onRemove: PropTypes.func.isRequired, 23 | completed: PropTypes.bool.isRequired, 24 | text: PropTypes.string.isRequired 25 | } 26 | 27 | export default TodoItem; 28 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/todos/reducer.js: -------------------------------------------------------------------------------- 1 | import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO}from './actionTypes.js'; 2 | 3 | export default (state = [], action) => { 4 | switch(action.type) { 5 | case ADD_TODO: { 6 | return [ 7 | { 8 | id: action.id, 9 | text: action.text, 10 | completed: false 11 | }, 12 | ...state 13 | ] 14 | } 15 | case TOGGLE_TODO: { 16 | return state.map((todoItem) => { 17 | if (todoItem.id === action.id) { 18 | return {...todoItem, completed: !todoItem.completed}; 19 | } else { 20 | return todoItem; 21 | } 22 | }) 23 | } 24 | case REMOVE_TODO: { 25 | return state.filter((todoItem) => { 26 | return todoItem.id !== action.id; 27 | }) 28 | } 29 | default: { 30 | return state; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/todos/reducer.js: -------------------------------------------------------------------------------- 1 | import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO}from './actionTypes.js'; 2 | 3 | export default (state = [], action) => { 4 | switch(action.type) { 5 | case ADD_TODO: { 6 | return [ 7 | { 8 | id: action.id, 9 | text: action.text, 10 | completed: false 11 | }, 12 | ...state 13 | ] 14 | } 15 | case TOGGLE_TODO: { 16 | return state.map((todoItem) => { 17 | if (todoItem.id === action.id) { 18 | return {...todoItem, completed: !todoItem.completed}; 19 | } else { 20 | return todoItem; 21 | } 22 | }) 23 | } 24 | case REMOVE_TODO: { 25 | return state.filter((todoItem) => { 26 | return todoItem.id !== action.id; 27 | }) 28 | } 29 | default: { 30 | return state; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/todos/reducer.js: -------------------------------------------------------------------------------- 1 | import {ADD_TODO, TOGGLE_TODO, REMOVE_TODO}from './actionTypes.js'; 2 | 3 | export default (state = [], action) => { 4 | switch(action.type) { 5 | case ADD_TODO: { 6 | return [ 7 | { 8 | id: action.id, 9 | text: action.text, 10 | completed: false 11 | }, 12 | ...state 13 | ] 14 | } 15 | case TOGGLE_TODO: { 16 | return state.map((todoItem) => { 17 | if (todoItem.id === action.id) { 18 | return {...todoItem, completed: !todoItem.completed}; 19 | } else { 20 | return todoItem; 21 | } 22 | }) 23 | } 24 | case REMOVE_TODO: { 25 | return state.filter((todoItem) => { 26 | return todoItem.id !== action.id; 27 | }) 28 | } 29 | default: { 30 | return state; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-09/weather_with_promise_middleware/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'; 2 | 3 | //import thunkMiddleware from 'redux-thunk' 4 | import promiseMiddleware from './middleware/promise_middleware.js'; 5 | 6 | import {reducer as weatherReducer} from './weather/'; 7 | 8 | import Perf from 'react-addons-perf' 9 | 10 | const win = window; 11 | win.Perf = Perf 12 | 13 | const reducer = combineReducers({ 14 | weather: weatherReducer 15 | }); 16 | 17 | const middlewares = [promiseMiddleware]; 18 | if (process.env.NODE_ENV !== 'production') { 19 | middlewares.push(require('redux-immutable-state-invariant')()); 20 | } 21 | 22 | const storeEnhancers = compose( 23 | applyMiddleware(...middlewares), 24 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 25 | ); 26 | 27 | export default createStore(reducer, {}, storeEnhancers); 28 | 29 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/Store.js: -------------------------------------------------------------------------------- 1 | //import {createStore, compose} from 'redux'; 2 | //const reducer = f => f; 3 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'; 4 | import {routerReducer} from 'react-router-redux'; 5 | 6 | import resetEnhancer from './enhancer/reset.js'; 7 | 8 | const originalReducers = { 9 | routing: routerReducer 10 | } 11 | const reducer = combineReducers(originalReducers); 12 | 13 | const win = window; 14 | 15 | const middlewares = []; 16 | if (process.env.NODE_ENV !== 'production') { 17 | middlewares.push(require('redux-immutable-state-invariant')()); 18 | } 19 | 20 | const storeEnhancers = compose( 21 | resetEnhancer, 22 | applyMiddleware(...middlewares), 23 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 24 | ); 25 | 26 | const initialState = { 27 | }; 28 | const store = createStore(reducer, initialState, storeEnhancers); 29 | store._reducers = originalReducers; 30 | export default store; 31 | 32 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/Store.js: -------------------------------------------------------------------------------- 1 | //import {createStore, compose} from 'redux'; 2 | //const reducer = f => f; 3 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'; 4 | import {routerReducer} from 'react-router-redux'; 5 | 6 | import resetEnhancer from './enhancer/reset.js'; 7 | 8 | const originalReducers = { 9 | routing: routerReducer 10 | } 11 | const reducer = combineReducers(originalReducers); 12 | 13 | const win = window; 14 | 15 | const middlewares = []; 16 | if (process.env.NODE_ENV !== 'production') { 17 | middlewares.push(require('redux-immutable-state-invariant')()); 18 | } 19 | 20 | const storeEnhancers = compose( 21 | resetEnhancer, 22 | applyMiddleware(...middlewares), 23 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 24 | ); 25 | 26 | const initialState = { 27 | }; 28 | const store = createStore(reducer, initialState, storeEnhancers); 29 | store._reducers = originalReducers; 30 | export default store; 31 | 32 | -------------------------------------------------------------------------------- /chapter-06/hoc/test/proxy/AddNewPropsHOC.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {shallow} from 'enzyme'; 3 | 4 | import addNewPropsHOC from '../../src/proxy/addNewPropsHOC.js'; 5 | 6 | describe('addNewPropsHOC', () => { 7 | 8 | const DemoComponent = (props) => { 9 | return ( 10 |
    render something.
    11 | ); 12 | }; 13 | 14 | it('should pass new props to wrapped component', () => { 15 | const NewComponent = addNewPropsHOC(DemoComponent, {foo: 'bar'}); 16 | const wrapper = shallow(); 17 | const expectedComponent = 18 | 19 | expect(wrapper.contains(expectedComponent)).toEqual(true); 20 | }); 21 | 22 | it('should only overrides given props', () => { 23 | const NewComponent = addNewPropsHOC(DemoComponent, {foo: 'bar'}); 24 | const wrapper = shallow(); 25 | const expectedComponent = 26 | 27 | expect(wrapper.contains(expectedComponent)).toEqual(true); 28 | }); 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/Store.js: -------------------------------------------------------------------------------- 1 | //import {createStore, compose} from 'redux'; 2 | //const reducer = f => f; 3 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'; 4 | import {routerReducer} from 'react-router-redux'; 5 | 6 | import resetEnhancer from './enhancer/reset.js'; 7 | 8 | const configureStore = () => { 9 | const originalReducers = { 10 | routing: routerReducer 11 | } 12 | const reducer = combineReducers(originalReducers); 13 | 14 | const win = global.window; 15 | 16 | const middlewares = []; 17 | if (process.env.NODE_ENV !== 'production') { 18 | middlewares.push(require('redux-immutable-state-invariant')()); 19 | } 20 | 21 | const storeEnhancers = compose( 22 | resetEnhancer, 23 | applyMiddleware(...middlewares), 24 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 25 | ); 26 | 27 | const store = createStore(reducer, {}, storeEnhancers); 28 | store._reducers = originalReducers; 29 | 30 | return store; 31 | } 32 | 33 | export {configureStore}; 34 | 35 | -------------------------------------------------------------------------------- /chapter-06/hoc/test/inheritance/OnlyForLoggedInHOC.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {mount} from 'enzyme'; 3 | 4 | import onlyForLoggedinHOC from '../../src/inheritance/onlyForLoggedinHOC.js'; 5 | 6 | describe('RefsHOC', () => { 7 | class DemoComponent extends React.Component { 8 | render() { 9 | return
    do something
    ; 10 | } 11 | } 12 | 13 | const NewComponent = onlyForLoggedinHOC(DemoComponent); 14 | 15 | it('should render inner component if loggedIn', () => { 16 | const wrapper = mount(); 17 | 18 | expect(wrapper.find('div').length).toEqual(1); 19 | 20 | // Since we've using inheritance, DemoComponent is replaced by NewComponent in the 21 | // actual dom tree. 22 | expect(wrapper.contains()).toEqual(false); 23 | }); 24 | 25 | it('should NOT render inner component if no loggedIn', () => { 26 | const wrapper = mount(); 27 | 28 | expect(wrapper.find('div').length).toEqual(0); 29 | }); 30 | }); 31 | 32 | 33 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/todos/views/todoList.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import TodoItem from './todoItem.js'; 4 | import {selectVisibleTodos} from '../selector.js'; 5 | 6 | import TransitionGroup from 'react-addons-css-transition-group'; 7 | import './todoItem.css'; 8 | 9 | const TodoList = ({todos}) => { 10 | return ( 11 |
      12 | 13 | { 14 | todos.map((item) => ( 15 | 21 | )) 22 | } 23 | 24 |
    25 | ); 26 | }; 27 | 28 | TodoList.propTypes = { 29 | todos: PropTypes.array.isRequired 30 | }; 31 | 32 | const mapStateToProps = (state) => { 33 | return { 34 | todos: selectVisibleTodos(state) 35 | }; 36 | } 37 | 38 | export default connect(mapStateToProps)(TodoList); 39 | -------------------------------------------------------------------------------- /chapter-04/todo/src/filter/views/link.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {setFilter} from '../actions.js'; 4 | 5 | const Link = ({active, children, onClick}) => { 6 | if (active) { 7 | return {children}; 8 | } else { 9 | return ( 10 | { 11 | ev.preventDefault(); 12 | onClick(); 13 | }}> 14 | {children} 15 | 16 | ); 17 | } 18 | }; 19 | 20 | Link.propTypes = { 21 | active: PropTypes.bool.isRequired, 22 | children: PropTypes.node.isRequired, 23 | onClick: PropTypes.func.isRequired 24 | }; 25 | 26 | const mapStateToProps = (state, ownProps) => { 27 | return { 28 | active: state.filter === ownProps.filter 29 | } 30 | }; 31 | 32 | const mapDispatchToProps = (dispatch, ownProps) => ({ 33 | onClick: () => { 34 | dispatch(setFilter(ownProps.filter)); 35 | } 36 | }); 37 | 38 | export default connect(mapStateToProps, mapDispatchToProps)(Link); 39 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/filter/views/link.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {setFilter} from '../actions.js'; 4 | 5 | const Link = ({active, children, onClick}) => { 6 | if (active) { 7 | return {children}; 8 | } else { 9 | return ( 10 | { 11 | ev.preventDefault(); 12 | onClick(); 13 | }}> 14 | {children} 15 | 16 | ); 17 | } 18 | }; 19 | 20 | Link.propTypes = { 21 | active: PropTypes.bool.isRequired, 22 | children: PropTypes.node.isRequired, 23 | onClick: PropTypes.func.isRequired 24 | }; 25 | 26 | const mapStateToProps = (state, ownProps) => { 27 | return { 28 | active: state.filter === ownProps.filter 29 | } 30 | }; 31 | 32 | const mapDispatchToProps = (dispatch, ownProps) => ({ 33 | onClick: () => { 34 | dispatch(setFilter(ownProps.filter)); 35 | } 36 | }); 37 | 38 | export default connect(mapStateToProps, mapDispatchToProps)(Link); 39 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/filter/views/link.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {setFilter} from '../actions.js'; 4 | 5 | const Link = ({active, children, onClick}) => { 6 | if (active) { 7 | return {children}; 8 | } else { 9 | return ( 10 | { 11 | ev.preventDefault(); 12 | onClick(); 13 | }}> 14 | {children} 15 | 16 | ); 17 | } 18 | }; 19 | 20 | Link.propTypes = { 21 | active: PropTypes.bool.isRequired, 22 | children: PropTypes.node.isRequired, 23 | onClick: PropTypes.func.isRequired 24 | }; 25 | 26 | const mapStateToProps = (state, ownProps) => { 27 | return { 28 | active: state.filter === ownProps.filter 29 | } 30 | }; 31 | 32 | const mapDispatchToProps = (dispatch, ownProps) => ({ 33 | onClick: () => { 34 | dispatch(setFilter(ownProps.filter)); 35 | } 36 | }); 37 | 38 | export default connect(mapStateToProps, mapDispatchToProps)(Link); 39 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/filter/views/link.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {setFilter} from '../actions.js'; 4 | 5 | const Link = ({active, children, onClick}) => { 6 | if (active) { 7 | return {children}; 8 | } else { 9 | return ( 10 | { 11 | ev.preventDefault(); 12 | onClick(); 13 | }}> 14 | {children} 15 | 16 | ); 17 | } 18 | }; 19 | 20 | Link.propTypes = { 21 | active: PropTypes.bool.isRequired, 22 | children: PropTypes.node.isRequired, 23 | onClick: PropTypes.func.isRequired 24 | }; 25 | 26 | const mapStateToProps = (state, ownProps) => { 27 | return { 28 | active: state.filter === ownProps.filter 29 | } 30 | }; 31 | 32 | const mapDispatchToProps = (dispatch, ownProps) => ({ 33 | onClick: () => { 34 | dispatch(setFilter(ownProps.filter)); 35 | } 36 | }); 37 | 38 | export default connect(mapStateToProps, mapDispatchToProps)(Link); 39 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/filter/views/link.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {setFilter} from '../actions.js'; 4 | 5 | const Link = ({active, children, onClick}) => { 6 | if (active) { 7 | return {children}; 8 | } else { 9 | return ( 10 | { 11 | ev.preventDefault(); 12 | onClick(); 13 | }}> 14 | {children} 15 | 16 | ); 17 | } 18 | }; 19 | 20 | Link.propTypes = { 21 | active: PropTypes.bool.isRequired, 22 | children: PropTypes.node.isRequired, 23 | onClick: PropTypes.func.isRequired 24 | }; 25 | 26 | const mapStateToProps = (state, ownProps) => { 27 | return { 28 | active: state.filter === ownProps.filter 29 | } 30 | }; 31 | 32 | const mapDispatchToProps = (dispatch, ownProps) => ({ 33 | onClick: () => { 34 | dispatch(setFilter(ownProps.filter)); 35 | } 36 | }); 37 | 38 | export default connect(mapStateToProps, mapDispatchToProps)(Link); 39 | -------------------------------------------------------------------------------- /chapter-04/todo_controlled_component/src/filter/views/link.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {setFilter} from '../actions.js'; 4 | 5 | const Link = ({active, children, onClick}) => { 6 | if (active) { 7 | return {children}; 8 | } else { 9 | return ( 10 | { 11 | ev.preventDefault(); 12 | onClick(); 13 | }}> 14 | {children} 15 | 16 | ); 17 | } 18 | }; 19 | 20 | Link.propTypes = { 21 | active: PropTypes.bool.isRequired, 22 | children: PropTypes.node.isRequired, 23 | onClick: PropTypes.func.isRequired 24 | }; 25 | 26 | const mapStateToProps = (state, ownProps) => { 27 | return { 28 | active: state.filter === ownProps.filter 29 | } 30 | }; 31 | 32 | const mapDispatchToProps = (dispatch, ownProps) => ({ 33 | onClick: () => { 34 | dispatch(setFilter(ownProps.filter)); 35 | } 36 | }); 37 | 38 | export default connect(mapStateToProps, mapDispatchToProps)(Link); 39 | -------------------------------------------------------------------------------- /chapter-10/animation_types/simulate_requestAnimationFrame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 |
    15 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /chapter-06/hoc/test/proxy/ConnectHOC.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {mount} from 'enzyme'; 3 | 4 | import {createStore} from 'redux'; 5 | import {Provider} from 'react-redux'; 6 | 7 | import connectHOC from '../../src/proxy/connectHOC.js'; 8 | 9 | describe('connectHOC', () => { 10 | class DemoComponent extends React.Component { 11 | render() { 12 | return
    anything
    ; 13 | } 14 | } 15 | 16 | it('should pass mapped props', () => { 17 | const mapState = (state) => ({ 18 | foo: state.foo 19 | }); 20 | const connector = connectHOC(mapState); 21 | const Container = connector(DemoComponent); 22 | const initialState = {foo: 'bar'}; 23 | const store = createStore(f=>f, initialState); 24 | const wrapper = mount( 25 | 26 | 27 | 28 | ); 29 | 30 | expect(wrapper.find('DemoComponent').props()).toEqual({ 31 | foo: 'bar' 32 | }); 33 | 34 | expect(Container.displayName).toEqual('Connect(DemoComponent)'); 35 | 36 | }); 37 | 38 | }); 39 | 40 | 41 | -------------------------------------------------------------------------------- /chapter-09/reset_enhancer/test/reset.test.js: -------------------------------------------------------------------------------- 1 | import resetEnhancer from '../src/reset.js'; 2 | import {createStore} from 'redux'; 3 | 4 | describe('reset enhancer', () => { 5 | const configureStore = (reducer) => { 6 | return createStore(reducer, resetEnhancer); 7 | }; 8 | 9 | let store; 10 | 11 | beforeEach(() => { 12 | const reducer = (state, action) => ({payload: action.payload}); 13 | store = configureStore(reducer); 14 | }); 15 | 16 | it('should not break store functionality', () => { 17 | store.dispatch({type: 'any', payload: 'bar'}); 18 | 19 | expect(store.getState()).toEqual({payload: 'bar'}); 20 | }); 21 | 22 | it('reset', () => { 23 | it('should reset state and reducer', () => { 24 | const newReducer = (state, action) =>({newPayload: action.payload}); 25 | const newState = {newPayload: 'new'}; 26 | 27 | store.reset(newReducer, newState); 28 | expect(store.getState()).toEqual(newState); 29 | 30 | store.dispatch({type: 'any', payload: 'changed'}); 31 | expect(store.getState()).toEqual({newPayload: 'changed'}); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /chapter-07/weather_redux/src/weather/actions.js: -------------------------------------------------------------------------------- 1 | import {FETCH_STARTED, FETCH_SUCCESS, FETCH_FAILURE} from './actionTypes.js'; 2 | 3 | export const fetchWeatherStarted = () => ({ 4 | type: FETCH_STARTED 5 | }); 6 | 7 | export const fetchWeatherSuccess = (result) => ({ 8 | type: FETCH_SUCCESS, 9 | result 10 | }) 11 | 12 | export const fetchWeatherFailure = (error) => ({ 13 | type: FETCH_FAILURE, 14 | error 15 | }) 16 | 17 | export const fetchWeather = (cityCode) => { 18 | return (dispatch) => { 19 | const apiUrl = `/data/cityinfo/${cityCode}.html`; 20 | 21 | dispatch(fetchWeatherStarted()) 22 | 23 | return fetch(apiUrl).then((response) => { 24 | if (response.status !== 200) { 25 | throw new Error('Fail to get response with status ' + response.status); 26 | } 27 | 28 | response.json().then((responseJson) => { 29 | dispatch(fetchWeatherSuccess(responseJson.weatherinfo)); 30 | }).catch((error) => { 31 | dispatch(fetchWeatherFailure(error)); 32 | }); 33 | }).catch((error) => { 34 | dispatch(fetchWeatherFailure(error)); 35 | }) 36 | }; 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/src/components/Counter/view.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import {bindActionCreators} from 'redux'; 3 | import {increment, decrement} from './actions.js'; 4 | import {connect} from 'react-redux'; 5 | 6 | const buttonStyle = { 7 | margin: '10px' 8 | }; 9 | 10 | export const stateKey = 'counter'; 11 | 12 | function Counter({onIncrement, onDecrement, value}) { 13 | return ( 14 |
    15 | 16 | 17 | Count: {value} 18 |
    19 | ); 20 | } 21 | 22 | Counter.propTypes = { 23 | onIncrement: PropTypes.func.isRequired, 24 | onDecrement: PropTypes.func.isRequired, 25 | value: PropTypes.number.isRequired 26 | }; 27 | 28 | const mapStateToProps = (state) => ({ 29 | value: state[stateKey] || 0 30 | }) 31 | 32 | const mapDispatchToProps = (dispatch) => bindActionCreators({ 33 | onIncrement: increment, 34 | onDecrement: decrement 35 | }, dispatch); 36 | 37 | export default connect(mapStateToProps, mapDispatchToProps)(Counter); 38 | 39 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/src/components/Counter/view.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import {bindActionCreators} from 'redux'; 3 | import {increment, decrement} from './actions.js'; 4 | import {connect} from 'react-redux'; 5 | 6 | const buttonStyle = { 7 | margin: '10px' 8 | }; 9 | 10 | export const stateKey = 'counter'; 11 | 12 | function Counter({onIncrement, onDecrement, value}) { 13 | return ( 14 |
    15 | 16 | 17 | Count: {value} 18 |
    19 | ); 20 | } 21 | 22 | Counter.propTypes = { 23 | onIncrement: PropTypes.func.isRequired, 24 | onDecrement: PropTypes.func.isRequired, 25 | value: PropTypes.number.isRequired 26 | }; 27 | 28 | const mapStateToProps = (state) => ({ 29 | value: state[stateKey] || 0 30 | }) 31 | 32 | const mapDispatchToProps = (dispatch) => bindActionCreators({ 33 | onIncrement: increment, 34 | onDecrement: decrement 35 | }, dispatch); 36 | 37 | export default connect(mapStateToProps, mapDispatchToProps)(Counter); 38 | 39 | -------------------------------------------------------------------------------- /chapter-12/express_server/src/components/Counter/view.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import {bindActionCreators} from 'redux'; 3 | import {increment, decrement} from './actions.js'; 4 | import {connect} from 'react-redux'; 5 | 6 | const buttonStyle = { 7 | margin: '10px' 8 | }; 9 | 10 | export const stateKey = 'counter'; 11 | 12 | function Counter({onIncrement, onDecrement, value}) { 13 | return ( 14 |
    15 | 16 | 17 | Count: {value} 18 |
    19 | ); 20 | } 21 | 22 | Counter.propTypes = { 23 | onIncrement: PropTypes.func.isRequired, 24 | onDecrement: PropTypes.func.isRequired, 25 | value: PropTypes.number.isRequired 26 | }; 27 | 28 | const mapStateToProps = (state) => ({ 29 | value: state[stateKey] || 0 30 | }) 31 | 32 | const mapDispatchToProps = (dispatch) => bindActionCreators({ 33 | onIncrement: increment, 34 | onDecrement: decrement 35 | }, dispatch); 36 | 37 | export default connect(mapStateToProps, mapDispatchToProps)(Counter); 38 | 39 | -------------------------------------------------------------------------------- /chapter-11/react_router_basic/src/Routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Router, Route, IndexRoute, browserHistory} from 'react-router'; 3 | import {Provider} from 'react-redux'; 4 | 5 | import {syncHistoryWithStore} from 'react-router-redux'; 6 | 7 | import App from './pages/App.js'; 8 | import Home from './pages/Home.js'; 9 | import About from './pages/About.js'; 10 | import NotFound from './pages/NotFound.js'; 11 | import store from './Store.js'; 12 | 13 | const createElement = (Component, props) => { 14 | return ( 15 | 16 | 17 | 18 | ); 19 | }; 20 | 21 | const history = syncHistoryWithStore(browserHistory, store); 22 | //const history = browserHistory; 23 | 24 | const Routes = () => ( 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ); 34 | 35 | export default Routes; 36 | -------------------------------------------------------------------------------- /chapter-12/isomorphic/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI or in coverage mode 14 | if (!process.env.CI && argv.indexOf('--coverage') < 0) { 15 | argv.push('--watch'); 16 | } 17 | 18 | // A temporary hack to clear terminal correctly. 19 | // You can remove this after updating to Jest 18 when it's out. 20 | // https://github.com/facebook/jest/pull/2230 21 | var realWrite = process.stdout.write; 22 | var CLEAR = process.platform === 'win32' ? '\x1Bc' : '\x1B[2J\x1B[3J\x1B[H'; 23 | process.stdout.write = function(chunk, encoding, callback) { 24 | if (chunk === '\x1B[2J\x1B[H') { 25 | chunk = CLEAR; 26 | } 27 | return realWrite.call(this, chunk, encoding, callback); 28 | }; 29 | 30 | 31 | jest.run(argv); 32 | -------------------------------------------------------------------------------- /chapter-03/redux_basic/src/views/Summary.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import store from '../Store.js'; 4 | 5 | class Summary extends Component { 6 | constructor(props) { 7 | super(props); 8 | 9 | this.onChange = this.onChange.bind(this); 10 | 11 | this.state = this.getOwnState(); 12 | } 13 | 14 | onChange() { 15 | this.setState(this.getOwnState()); 16 | } 17 | 18 | getOwnState() { 19 | const state = store.getState(); 20 | let sum = 0; 21 | for (const key in state) { 22 | if (state.hasOwnProperty(key)) { 23 | sum += state[key]; 24 | } 25 | } 26 | 27 | return { sum: sum }; 28 | } 29 | 30 | shouldComponentUpdate(nextProps, nextState) { 31 | return nextState.sum !== this.state.sum; 32 | } 33 | 34 | componentDidMount() { 35 | store.subscribe(this.onChange); 36 | } 37 | 38 | componentWillUnmount() { 39 | store.unsubscribe(this.onChange); 40 | } 41 | 42 | render() { 43 | const sum = this.state.sum; 44 | return ( 45 |
    Total Count: {sum}
    46 | ); 47 | } 48 | } 49 | 50 | export default Summary; 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /chapter-12/express_server/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI or in coverage mode 14 | if (!process.env.CI && argv.indexOf('--coverage') < 0) { 15 | argv.push('--watch'); 16 | } 17 | 18 | // A temporary hack to clear terminal correctly. 19 | // You can remove this after updating to Jest 18 when it's out. 20 | // https://github.com/facebook/jest/pull/2230 21 | var realWrite = process.stdout.write; 22 | var CLEAR = process.platform === 'win32' ? '\x1Bc' : '\x1B[2J\x1B[3J\x1B[H'; 23 | process.stdout.write = function(chunk, encoding, callback) { 24 | if (chunk === '\x1B[2J\x1B[H') { 25 | chunk = CLEAR; 26 | } 27 | return realWrite.call(this, chunk, encoding, callback); 28 | }; 29 | 30 | 31 | jest.run(argv); 32 | -------------------------------------------------------------------------------- /chapter-11/chunked_with_redux/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI or in coverage mode 14 | if (!process.env.CI && argv.indexOf('--coverage') < 0) { 15 | argv.push('--watch'); 16 | } 17 | 18 | // A temporary hack to clear terminal correctly. 19 | // You can remove this after updating to Jest 18 when it's out. 20 | // https://github.com/facebook/jest/pull/2230 21 | var realWrite = process.stdout.write; 22 | var CLEAR = process.platform === 'win32' ? '\x1Bc' : '\x1B[2J\x1B[3J\x1B[H'; 23 | process.stdout.write = function(chunk, encoding, callback) { 24 | if (chunk === '\x1B[2J\x1B[H') { 25 | chunk = CLEAR; 26 | } 27 | return realWrite.call(this, chunk, encoding, callback); 28 | }; 29 | 30 | 31 | jest.run(argv); 32 | -------------------------------------------------------------------------------- /chapter-11/react_router_chunked/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI or in coverage mode 14 | if (!process.env.CI && argv.indexOf('--coverage') < 0) { 15 | argv.push('--watch'); 16 | } 17 | 18 | // A temporary hack to clear terminal correctly. 19 | // You can remove this after updating to Jest 18 when it's out. 20 | // https://github.com/facebook/jest/pull/2230 21 | var realWrite = process.stdout.write; 22 | var CLEAR = process.platform === 'win32' ? '\x1Bc' : '\x1B[2J\x1B[3J\x1B[H'; 23 | process.stdout.write = function(chunk, encoding, callback) { 24 | if (chunk === '\x1B[2J\x1B[H') { 25 | chunk = CLEAR; 26 | } 27 | return realWrite.call(this, chunk, encoding, callback); 28 | }; 29 | 30 | 31 | jest.run(argv); 32 | -------------------------------------------------------------------------------- /chapter-05/todo_perf/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'; 2 | 3 | import {reducer as todoReducer} from './todos'; 4 | import {reducer as filterReducer} from './filter'; 5 | 6 | import Perf from 'react-addons-perf' 7 | 8 | const win = window; 9 | win.Perf = Perf 10 | 11 | const reducer = combineReducers({ 12 | todos: todoReducer, 13 | filter: filterReducer 14 | }); 15 | 16 | const middlewares = []; 17 | if (process.env.NODE_ENV !== 'production') { 18 | middlewares.push(require('redux-immutable-state-invariant')()); 19 | } 20 | 21 | const storeEnhancers = compose( 22 | applyMiddleware(...middlewares), 23 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 24 | ); 25 | 26 | const initialState = { 27 | todos: [ 28 | { 29 | id: 0, 30 | text: 'First', 31 | completed: true 32 | }, 33 | { 34 | id: 1, 35 | text: 'Second', 36 | completed: false 37 | }, 38 | { 39 | id: 2, 40 | text: 'Third', 41 | completed: true 42 | } 43 | ] 44 | 45 | } 46 | export default createStore(reducer, initialState, storeEnhancers); 47 | 48 | -------------------------------------------------------------------------------- /chapter-10/todo_animated/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'; 2 | 3 | import {reducer as todoReducer} from './todos'; 4 | import {reducer as filterReducer} from './filter'; 5 | 6 | import Perf from 'react-addons-perf' 7 | 8 | const win = window; 9 | win.Perf = Perf 10 | 11 | const reducer = combineReducers({ 12 | todos: todoReducer, 13 | filter: filterReducer 14 | }); 15 | 16 | const middlewares = []; 17 | if (process.env.NODE_ENV !== 'production') { 18 | middlewares.push(require('redux-immutable-state-invariant')()); 19 | } 20 | 21 | const storeEnhancers = compose( 22 | applyMiddleware(...middlewares), 23 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 24 | ); 25 | 26 | const initialState = { 27 | todos: [ 28 | { 29 | id: 0, 30 | text: 'First', 31 | completed: true 32 | }, 33 | { 34 | id: 1, 35 | text: 'Second', 36 | completed: false 37 | }, 38 | { 39 | id: 2, 40 | text: 'Third', 41 | completed: true 42 | } 43 | ] 44 | 45 | } 46 | export default createStore(reducer, initialState, storeEnhancers); 47 | 48 | -------------------------------------------------------------------------------- /chapter-10/todo_react_motion/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'; 2 | 3 | import {reducer as todoReducer} from './todos'; 4 | import {reducer as filterReducer} from './filter'; 5 | 6 | import Perf from 'react-addons-perf' 7 | 8 | const win = window; 9 | win.Perf = Perf 10 | 11 | const reducer = combineReducers({ 12 | todos: todoReducer, 13 | filter: filterReducer 14 | }); 15 | 16 | const middlewares = []; 17 | if (process.env.NODE_ENV !== 'production') { 18 | middlewares.push(require('redux-immutable-state-invariant')()); 19 | } 20 | 21 | const storeEnhancers = compose( 22 | applyMiddleware(...middlewares), 23 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 24 | ); 25 | 26 | const initialState = { 27 | todos: [ 28 | { 29 | id: 0, 30 | text: 'First', 31 | completed: true 32 | }, 33 | { 34 | id: 1, 35 | text: 'Second', 36 | completed: false 37 | }, 38 | { 39 | id: 2, 40 | text: 'Third', 41 | completed: true 42 | } 43 | ] 44 | 45 | } 46 | export default createStore(reducer, initialState, storeEnhancers); 47 | 48 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/src/Store.js: -------------------------------------------------------------------------------- 1 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux'; 2 | 3 | import {reducer as todoReducer} from './todos'; 4 | import {reducer as filterReducer} from './filter'; 5 | 6 | import Perf from 'react-addons-perf' 7 | 8 | const win = window; 9 | win.Perf = Perf 10 | 11 | const reducer = combineReducers({ 12 | todos: todoReducer, 13 | filter: filterReducer 14 | }); 15 | 16 | const middlewares = []; 17 | if (process.env.NODE_ENV !== 'production') { 18 | middlewares.push(require('redux-immutable-state-invariant')()); 19 | } 20 | 21 | const storeEnhancers = compose( 22 | applyMiddleware(...middlewares), 23 | (win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f, 24 | ); 25 | 26 | const initialState = { 27 | todos: [ 28 | { 29 | id: 0, 30 | text: 'First', 31 | completed: true 32 | }, 33 | { 34 | id: 1, 35 | text: 'Second', 36 | completed: false 37 | }, 38 | { 39 | id: 2, 40 | text: 'Third', 41 | completed: true 42 | } 43 | ] 44 | 45 | } 46 | export default createStore(reducer, initialState, storeEnhancers); 47 | 48 | -------------------------------------------------------------------------------- /chapter-06/function_as_child/src/CountDown.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class CountDown extends React.Component { 4 | 5 | constructor() { 6 | super(...arguments); 7 | 8 | this.state = {count: this.props.startCount}; 9 | } 10 | 11 | shouldComponentUpdate(nextProps, nextState) { 12 | return nextState.count !== this.state.count; 13 | } 14 | 15 | componentDidMount() { 16 | this.intervalHandle = setInterval(() => { 17 | const newCount = this.state.count - 1; 18 | if (newCount >= 0) { 19 | this.setState({count: newCount}); 20 | } else { 21 | window.clearInterval(this.intervalHandle); 22 | this.intervalHandle = null; 23 | } 24 | }, 1000); 25 | } 26 | 27 | componentWillUnmount() { 28 | if (this.intervalHandle) { 29 | window.clearInterval(this.intervalHandle); 30 | this.intervalHandle = null; 31 | } 32 | } 33 | 34 | render() { 35 | return this.props.children(this.state.count); 36 | } 37 | } 38 | 39 | CountDown.propTypes = { 40 | children: React.PropTypes.func.isRequired, 41 | startCount: React.PropTypes.number.isRequired 42 | } 43 | 44 | export default CountDown; 45 | -------------------------------------------------------------------------------- /chapter-03/flux/src/stores/CounterStore.js: -------------------------------------------------------------------------------- 1 | import AppDispatcher from '../AppDispatcher.js'; 2 | import * as ActionTypes from '../ActionTypes.js'; 3 | import {EventEmitter} from 'events'; 4 | 5 | const CHANGE_EVENT = 'changed'; 6 | 7 | const counterValues = { 8 | 'First': 0, 9 | 'Second': 10, 10 | 'Third': 30 11 | }; 12 | 13 | 14 | const CounterStore = Object.assign({}, EventEmitter.prototype, { 15 | getCounterValues: function() { 16 | return counterValues; 17 | }, 18 | 19 | emitChange: function() { 20 | this.emit(CHANGE_EVENT); 21 | }, 22 | 23 | addChangeListener: function(callback) { 24 | this.on(CHANGE_EVENT, callback); 25 | }, 26 | 27 | removeChangeListener: function(callback) { 28 | this.removeListener(CHANGE_EVENT, callback); 29 | } 30 | 31 | }); 32 | 33 | CounterStore.dispatchToken = AppDispatcher.register((action) => { 34 | if (action.type === ActionTypes.INCREMENT) { 35 | counterValues[action.counterCaption] ++; 36 | CounterStore.emitChange(); 37 | } else if (action.type === ActionTypes.DECREMENT) { 38 | counterValues[action.counterCaption] --; 39 | CounterStore.emitChange(); 40 | } 41 | }); 42 | 43 | export default CounterStore; 44 | -------------------------------------------------------------------------------- /chapter-05/todo_with_selector/test/todos/views/todoList.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {mount} from 'enzyme'; 3 | import {createStore, combineReducers} from 'redux'; 4 | import {Provider} from 'react-redux'; 5 | 6 | import {reducer as todosReducer, actions} from '../../../src/todos/index.js'; 7 | import {reducer as filterReducer} from '../../../src/filter/index.js'; 8 | import {FilterTypes} from '../../../src/constants.js'; 9 | import TodoList from '../../../src/todos/views/todoList.js'; 10 | import TodoItem from '../../../src/todos/views/todoItem.js'; 11 | 12 | describe('todos', () => { 13 | it('should add new todo-item on addTodo action', () => { 14 | const store = createStore( 15 | combineReducers({ 16 | todos: todosReducer, 17 | filter: filterReducer 18 | }), { 19 | todos: [], 20 | filter: FilterTypes.ALL 21 | }); 22 | const subject = ( 23 | 24 | 25 | 26 | ); 27 | const wrapper = mount(subject); 28 | 29 | store.dispatch(actions.addTodo('write more test')); 30 | 31 | expect(wrapper.find('.text').text()).toEqual('write more test'); 32 | }); 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /chapter-06/hoc/test/inheritance/ModifyPropsHOC.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {mount} from 'enzyme'; 3 | 4 | import chai from 'chai'; 5 | import chaiEnzyme from 'chai-enzyme'; 6 | chai.use(chaiEnzyme()); 7 | 8 | const {expect} = chai; 9 | 10 | import modifyPropsHOC from '../../src/inheritance/modifyPropsHOC.js'; 11 | 12 | describe('modifyPropsHOC', () => { 13 | 14 | it('should render with modified props as red', () => { 15 | class DivComponent extends React.Component { 16 | render () { 17 | return
    hello world
    ; 18 | } 19 | } 20 | const NewComponent = modifyPropsHOC(DivComponent); 21 | const wrapper = mount(); 22 | 23 | expect(wrapper.find('div')).to.have.style('color').equal('red'); 24 | }); 25 | 26 | it('should render with modified props as green', () => { 27 | class SpanComponent extends React.Component { 28 | render () { 29 | return hello world; 30 | } 31 | } 32 | const NewComponent = modifyPropsHOC(SpanComponent); 33 | const wrapper = mount(); 34 | 35 | expect(wrapper.find('span')).to.have.style('color').equal('green'); 36 | }); 37 | 38 | 39 | }); 40 | 41 | 42 | -------------------------------------------------------------------------------- /chapter-02/controlpanel_with_summary/src/ControlPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Counter from './Counter.js'; 3 | 4 | const style = { 5 | margin: '20px' 6 | }; 7 | 8 | class ControlPanel extends Component { 9 | 10 | constructor(props) { 11 | super(props); 12 | 13 | this.onCounterUpdate = this.onCounterUpdate.bind(this); 14 | 15 | this.initValues = [ 0, 10, 20]; 16 | 17 | const initSum = this.initValues.reduce((a, b) => a+b, 0); 18 | this.state = { 19 | sum: initSum 20 | }; 21 | } 22 | 23 | onCounterUpdate(newValue, previousValue) { 24 | const valueChange = newValue - previousValue; 25 | this.setState({ sum: this.state.sum + valueChange}); 26 | } 27 | 28 | render() { 29 | return ( 30 |
    31 | 32 | 33 | 34 |
    35 |
    Total Count: {this.state.sum}
    36 |
    37 | ); 38 | } 39 | } 40 | 41 | export default ControlPanel; 42 | 43 | -------------------------------------------------------------------------------- /chapter-03/flux/config/env.js: -------------------------------------------------------------------------------- 1 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 2 | // injected into the application via DefinePlugin in Webpack configuration. 3 | 4 | var REACT_APP = /^REACT_APP_/i; 5 | 6 | function getClientEnvironment(publicUrl) { 7 | var processEnv = Object 8 | .keys(process.env) 9 | .filter(key => REACT_APP.test(key)) 10 | .reduce((env, key) => { 11 | env[key] = JSON.stringify(process.env[key]); 12 | return env; 13 | }, { 14 | // Useful for determining whether we’re running in production mode. 15 | // Most importantly, it switches React into the correct mode. 16 | 'NODE_ENV': JSON.stringify( 17 | process.env.NODE_ENV || 'development' 18 | ), 19 | // Useful for resolving the correct path to static assets in `public`. 20 | // For example, . 21 | // This should only be used as an escape hatch. Normally you would put 22 | // images into the `src` and `import` them in code to get their paths. 23 | 'PUBLIC_URL': JSON.stringify(publicUrl) 24 | }); 25 | return {'process.env': processEnv}; 26 | } 27 | 28 | module.exports = getClientEnvironment; 29 | --------------------------------------------------------------------------------