Created
November 27, 2015 21:55
-
-
Save anonymous/5bd892cda7c82762790c to your computer and use it in GitHub Desktop.
JS Bin // source http://jsbin.com/wuwezo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<script src="https://fb.me/react-0.14.3.min.js"></script> | |
<script src="https://fb.me/react-dom-0.14.3.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.0.4/redux.min.js"></script> | |
<meta charset="utf-8"> | |
<title>JS Bin</title> | |
</head> | |
<body> | |
<div id="root"></div> | |
<script id="jsbin-javascript"> | |
// 3rd level reducer. | |
// Here, `state` refers to a single todo object. | |
'use strict'; | |
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | |
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; | |
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | |
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } } | |
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } | |
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } | |
var todo = function todo(state, action) { | |
console.log("todo reducer called"); | |
// Remember, no mutation. | |
// Initial state is considered (or not, in the ADD_TODO case), | |
// and used to construct a new state object, always. | |
switch (action.type) { | |
case 'ADD_TODO': | |
console.log('todo ADD_TODO'); | |
return { | |
id: action.id, | |
text: action.text, | |
completed: false | |
}; | |
case 'TOGGLE_TODO': | |
console.log('todo TOGGLE_TODO'); | |
// Bail out if the action is for a different todo than the one passed in. | |
if (state.id !== action.id) { | |
return state; | |
} | |
// ... below is ES7 "Object Rest Destructuring" | |
// https://github.com/sebmarkbage/ecmascript-rest-spread/blob/master/Spread.md | |
// Think of it as "and the rest", or "override these object properties" | |
return _extends({}, state, { | |
completed: !state.completed | |
}); | |
default: | |
return state; | |
} | |
}; | |
// 2nd level reducer | |
// Here, `state` refers to the array of todo objects | |
var todos = function todos(state, action) { | |
if (state === undefined) state = []; | |
console.log("todos reducer called"); | |
switch (action.type) { | |
// Returns a new array of todos, containing the added todo as described by `action`. | |
// The new todo is constructed by delegating to the `todo` reducer. | |
case 'ADD_TODO': | |
console.log('todos ADD_TODO'); | |
// ... below is ES6 "spread operator" (arrays only) | |
return [].concat(_toConsumableArray(state), [todo(undefined, action)]); | |
// Returns a new array of todos, with an individual todo's completed status | |
// toggled as identified by `action.id`. | |
// Must operate on entire list (seems wrong somehow). | |
case 'TOGGLE_TODO': | |
console.log('todos TOGGLE_TODO'); | |
return state.map(function (t) { | |
return todo(t, action); | |
}); | |
default: | |
return state; | |
} | |
}; | |
// 2nd level reducer | |
// Here, `state` refers to a simple configuration string (enum/atom) | |
// Remember, we are only returning the state we are concerned with | |
var visibilityFilter = function visibilityFilter(state, action) { | |
if (state === undefined) state = 'SHOW_ALL'; | |
console.log("visibilityFilter reducer called"); | |
switch (action.type) { | |
case 'SET_VISIBILITY_FILTER': | |
return action.filter; | |
default: | |
return state; | |
} | |
}; | |
// Top level reducer. | |
// combineReducers is not magic. | |
var _Redux = Redux; | |
var combineReducers = _Redux.combineReducers; | |
var todoApp = combineReducers({ | |
// n.b. following syntax is ES6 Object Initializer (shorthand property names) | |
todos: todos, // todos: todos, | |
visibilityFilter: visibilityFilter // visibilityFilter: visibilityFilter | |
}); | |
var _Redux2 = Redux; | |
var createStore = _Redux2.createStore; | |
var store = createStore(todoApp); | |
var _React = React; | |
var Component = _React.Component; | |
// This looks like just a function, not a React component. (Right?) | |
// No! This is a "stateless component". It gets `props` passed in. | |
// https://facebook.github.io/react/blog/2015/09/10/react-v0.14-rc1.html#stateless-function-components | |
// Notice the curlybraces in the parameter list. This is destructuring | |
// `props` (e.g. filter = props.filter, children = props.children) | |
var Link = function Link(_ref) { | |
var active = _ref.active; | |
var children = _ref.children; | |
var onClick = _ref.onClick; | |
if (active) return React.createElement( | |
'span', | |
null, | |
children | |
); | |
return React.createElement( | |
'a', | |
{ href: '#', | |
onClick: function (e) { | |
e.preventDefault(); | |
onClick(filter); | |
} | |
}, | |
children | |
); | |
}; | |
// Container component. Provides data and behavior for presentational components. | |
// When it mounts, it subscribes to store changes, allowing it to independently | |
// re-render. It delegates presnetation to the Link component. | |
var FilterLink = (function (_Component) { | |
_inherits(FilterLink, _Component); | |
function FilterLink() { | |
_classCallCheck(this, FilterLink); | |
_get(Object.getPrototypeOf(FilterLink.prototype), 'constructor', this).apply(this, arguments); | |
} | |
_createClass(FilterLink, [{ | |
key: 'componentDidMount', | |
value: function componentDidMount() { | |
var _this = this; | |
// n.b. Reads current state, but not subscribed. | |
// If parent component does not update when store is updated, will render stale value. | |
// Currently we re-render entire application when any state changes, but this is not efficient. | |
// We will instead move store subscription to the lifecycle methods of container copmonents. | |
// React provies a special forceUpdate method on component instances to force their rerendering. | |
this.unsubscribe = store.subscribe(function () { | |
_this.forceUpdate(); | |
}); | |
} | |
}, { | |
key: 'componentWillUnmount', | |
value: function componentWillUnmount() { | |
this.unsubscribe(); | |
} | |
}, { | |
key: 'render', | |
value: function render() { | |
var props = this.props; | |
var state = store.getState(); | |
return React.createElement( | |
Link, | |
{ | |
active: props.filter === state.visibilityFilter, | |
onClick: function () { | |
store.dispatch({ | |
type: 'SET_VISIBILITY_FILTER', | |
filter: props.filter | |
}); | |
} | |
}, | |
props.children | |
); | |
} | |
}]); | |
return FilterLink; | |
})(Component); | |
var AddTodo = function AddTodo(_ref2) { | |
var onAddClick = _ref2.onAddClick; | |
var input = undefined; | |
return React.createElement( | |
'div', | |
null, | |
React.createElement('input', { ref: function (node) { | |
// This is a newer 0.14 syntax where ref can be a callback. | |
// n.b. no `this` available in a functional/stateless component. | |
// Here, we're making a closure over `input`. | |
input = node; | |
} }), | |
React.createElement( | |
'button', | |
{ onClick: function () { | |
onAddClick(input.value); | |
input.value = ''; | |
} }, | |
'Add todo' | |
) | |
); | |
}; | |
var Footer = function Footer() { | |
return React.createElement( | |
'p', | |
null, | |
'Show:', | |
' ', | |
React.createElement( | |
FilterLink, | |
{ | |
filter: 'SHOW_ALL' }, | |
'All' | |
), | |
' ', | |
React.createElement( | |
FilterLink, | |
{ | |
filter: 'SHOW_ACTIVE' }, | |
'Active' | |
), | |
' ', | |
React.createElement( | |
FilterLink, | |
{ | |
filter: 'SHOW_COMPLETED' }, | |
'Completed' | |
) | |
); | |
}; | |
var getVisibleTodos = function getVisibleTodos(todos, filter) { | |
console.log('getVisibleTodos: todos=' + todos + ' filter=' + filter); | |
switch (filter) { | |
case 'SHOW_ALL': | |
return todos; | |
case 'SHOW_COMPLETED': | |
return todos.filter(function (t) { | |
return t.completed; | |
}); | |
case 'SHOW_ACTIVE': | |
return todos.filter(function (t) { | |
return !t.completed; | |
}); | |
} | |
}; | |
var nextToDoId = 0; // global :shrug: | |
var TodoApp = function TodoApp(_ref3) { | |
var todos = _ref3.todos; | |
var visibilityFilter = _ref3.visibilityFilter; | |
return React.createElement( | |
'div', | |
null, | |
React.createElement(AddTodo, { | |
onAddClick: function (text) { | |
store.dispatch({ | |
type: 'ADD_TODO', | |
id: nextToDoId++, | |
text: text | |
}); | |
} | |
}), | |
React.createElement(TodoList, { | |
todos: getVisibleTodos(todos, visibilityFilter), | |
onTodoClick: function (id) { | |
store.dispatch({ | |
type: 'TOGGLE_TODO', | |
id: id | |
}); | |
} | |
}), | |
React.createElement(Footer, null) | |
); | |
}; | |
var Todo = function Todo(_ref4) { | |
var onClick = _ref4.onClick; | |
var completed = _ref4.completed; | |
var text = _ref4.text; | |
return React.createElement( | |
'li', | |
{ | |
onClick: onClick, | |
style: { textDecoration: completed ? 'line-through' : 'none' | |
} | |
}, | |
text | |
); | |
}; | |
var TodoList = function TodoList(_ref5) { | |
var todos = _ref5.todos; | |
var onTodoClick = _ref5.onTodoClick; | |
return React.createElement( | |
'ul', | |
null, | |
todos.map(function (todo) { | |
return React.createElement(Todo, _extends({ | |
key: todo.id | |
}, todo, { | |
onClick: function () { | |
return onTodoClick(todo.id); | |
} | |
})); | |
}) | |
); | |
}; | |
var render = function render() { | |
console.log('render called'); | |
// JSX Spread Attributes. https://facebook.github.io/react/docs/jsx-spread.html | |
// <TodoApp {...store.getState()} /> | |
// is equivalent to: | |
// <ToDoApp | |
// todos={this.store.getState().props} | |
// visibilityFilter={this.store.getState().visibilityFilter} /> | |
ReactDOM.render(React.createElement(TodoApp, store.getState()), document.getElementById('root')); | |
}; | |
// Call render every time the store has an action dispatched | |
store.subscribe(render); | |
// Call render once for initial display | |
render(); | |
</script> | |
<script id="jsbin-source-javascript" type="text/javascript">// 3rd level reducer. | |
// Here, `state` refers to a single todo object. | |
const todo = (state, action) => { | |
console.log("todo reducer called"); | |
// Remember, no mutation. | |
// Initial state is considered (or not, in the ADD_TODO case), | |
// and used to construct a new state object, always. | |
switch (action.type) { | |
case 'ADD_TODO': | |
console.log('todo ADD_TODO'); | |
return { | |
id: action.id, | |
text: action.text, | |
completed: false | |
}; | |
case 'TOGGLE_TODO': | |
console.log('todo TOGGLE_TODO'); | |
// Bail out if the action is for a different todo than the one passed in. | |
if (state.id !== action.id) { | |
return state; | |
} | |
// ... below is ES7 "Object Rest Destructuring" | |
// https://github.com/sebmarkbage/ecmascript-rest-spread/blob/master/Spread.md | |
// Think of it as "and the rest", or "override these object properties" | |
return { | |
...state, | |
completed: !state.completed | |
}; | |
default: | |
return state; | |
} | |
} | |
// 2nd level reducer | |
// Here, `state` refers to the array of todo objects | |
const todos = (state = [], action) => { | |
console.log("todos reducer called"); | |
switch (action.type) { | |
// Returns a new array of todos, containing the added todo as described by `action`. | |
// The new todo is constructed by delegating to the `todo` reducer. | |
case 'ADD_TODO': | |
console.log('todos ADD_TODO'); | |
// ... below is ES6 "spread operator" (arrays only) | |
return [ | |
...state, | |
todo(undefined, action) | |
]; | |
// Returns a new array of todos, with an individual todo's completed status | |
// toggled as identified by `action.id`. | |
// Must operate on entire list (seems wrong somehow). | |
case 'TOGGLE_TODO': | |
console.log('todos TOGGLE_TODO'); | |
return state.map(t => | |
todo(t, action) | |
); | |
default: | |
return state; | |
} | |
} | |
// 2nd level reducer | |
// Here, `state` refers to a simple configuration string (enum/atom) | |
// Remember, we are only returning the state we are concerned with | |
const visibilityFilter = (state = 'SHOW_ALL', action) => { | |
console.log("visibilityFilter reducer called"); | |
switch (action.type) { | |
case 'SET_VISIBILITY_FILTER': | |
return action.filter; | |
default: | |
return state; | |
} | |
} | |
// Top level reducer. | |
// combineReducers is not magic. | |
const { combineReducers } = Redux; | |
const todoApp = combineReducers({ | |
// n.b. following syntax is ES6 Object Initializer (shorthand property names) | |
todos, // todos: todos, | |
visibilityFilter // visibilityFilter: visibilityFilter | |
}); | |
const { createStore } = Redux; | |
const store = createStore(todoApp); | |
const { Component } = React; | |
// This looks like just a function, not a React component. (Right?) | |
// No! This is a "stateless component". It gets `props` passed in. | |
// https://facebook.github.io/react/blog/2015/09/10/react-v0.14-rc1.html#stateless-function-components | |
// Notice the curlybraces in the parameter list. This is destructuring | |
// `props` (e.g. filter = props.filter, children = props.children) | |
const Link = ({ | |
active, | |
children, | |
onClick | |
}) => { | |
if (active) | |
return <span>{children}</span>; | |
return ( | |
<a href='#' | |
onClick={e => { | |
e.preventDefault(); | |
onClick(filter); | |
}} | |
> | |
{children} | |
</a> | |
); | |
}; | |
// Container component. Provides data and behavior for presentational components. | |
// When it mounts, it subscribes to store changes, allowing it to independently | |
// re-render. It delegates presnetation to the Link component. | |
class FilterLink extends Component { | |
componentDidMount() { | |
// n.b. Reads current state, but not subscribed. | |
// If parent component does not update when store is updated, will render stale value. | |
// Currently we re-render entire application when any state changes, but this is not efficient. | |
// We will instead move store subscription to the lifecycle methods of container copmonents. | |
// React provies a special forceUpdate method on component instances to force their rerendering. | |
this.unsubscribe = store.subscribe(() => { | |
this.forceUpdate(); | |
}); | |
} | |
componentWillUnmount() { | |
this.unsubscribe(); | |
} | |
render() { | |
const props = this.props; | |
const state = store.getState(); | |
return ( | |
<Link | |
active={ | |
props.filter === state.visibilityFilter | |
} | |
onClick={() => { | |
store.dispatch({ | |
type: 'SET_VISIBILITY_FILTER', | |
filter: props.filter | |
}); | |
}} | |
> | |
{props.children} | |
</Link> | |
); | |
} | |
} | |
const AddTodo = ({ | |
onAddClick | |
}) => { | |
let input; | |
return ( | |
<div> | |
<input ref={node => { | |
// This is a newer 0.14 syntax where ref can be a callback. | |
// n.b. no `this` available in a functional/stateless component. | |
// Here, we're making a closure over `input`. | |
input = node; | |
}} /> | |
<button onClick={() => { | |
onAddClick(input.value); | |
input.value = ''; | |
}}> | |
Add todo | |
</button> | |
</div> | |
); | |
} | |
const Footer = () => { | |
return ( | |
<p> | |
Show: | |
{' '} | |
<FilterLink | |
filter='SHOW_ALL'>All</FilterLink> | |
{' '} | |
<FilterLink | |
filter='SHOW_ACTIVE'>Active</FilterLink> | |
{' '} | |
<FilterLink | |
filter='SHOW_COMPLETED'>Completed</FilterLink> | |
</p> | |
); | |
} | |
const getVisibleTodos = (todos, filter) => { | |
console.log(`getVisibleTodos: todos=${todos} filter=${filter}`); | |
switch (filter) { | |
case 'SHOW_ALL': | |
return todos; | |
case 'SHOW_COMPLETED': | |
return todos.filter(t => t.completed); | |
case 'SHOW_ACTIVE': | |
return todos.filter(t => !t.completed); | |
} | |
} | |
let nextToDoId = 0; // global :shrug: | |
const TodoApp = ({ | |
todos, | |
visibilityFilter | |
}) => { | |
return ( | |
<div> | |
<AddTodo | |
onAddClick={(text) => { | |
store.dispatch({ | |
type: 'ADD_TODO', | |
id: nextToDoId++, | |
text | |
}); | |
}} | |
/> | |
<TodoList | |
todos={getVisibleTodos(todos, visibilityFilter)} | |
onTodoClick={(id) => { | |
store.dispatch({ | |
type: 'TOGGLE_TODO', | |
id | |
}); | |
}} | |
/> | |
<Footer /> | |
</div> | |
); | |
}; | |
const Todo = ({ | |
onClick, | |
completed, | |
text | |
}) => { | |
return ( | |
<li | |
onClick={onClick} | |
style={{textDecoration: | |
completed ? | |
'line-through' : | |
'none' | |
}} | |
> | |
{text} | |
</li> | |
); | |
}; | |
const TodoList = ({ | |
todos, | |
onTodoClick | |
}) => ( | |
<ul> | |
{todos.map(todo => | |
<Todo | |
key={todo.id} | |
{...todo} | |
onClick={() => onTodoClick(todo.id)} | |
/> | |
)} | |
</ul> | |
); | |
const render = () => { | |
console.log('render called'); | |
// JSX Spread Attributes. https://facebook.github.io/react/docs/jsx-spread.html | |
// <TodoApp {...store.getState()} /> | |
// is equivalent to: | |
// <ToDoApp | |
// todos={this.store.getState().props} | |
// visibilityFilter={this.store.getState().visibilityFilter} /> | |
ReactDOM.render( | |
<TodoApp | |
{...store.getState()} | |
/>, | |
document.getElementById('root') | |
); | |
}; | |
// Call render every time the store has an action dispatched | |
store.subscribe(render); | |
// Call render once for initial display | |
render();</script></body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 3rd level reducer. | |
// Here, `state` refers to a single todo object. | |
'use strict'; | |
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | |
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; | |
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | |
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } } | |
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } | |
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } | |
var todo = function todo(state, action) { | |
console.log("todo reducer called"); | |
// Remember, no mutation. | |
// Initial state is considered (or not, in the ADD_TODO case), | |
// and used to construct a new state object, always. | |
switch (action.type) { | |
case 'ADD_TODO': | |
console.log('todo ADD_TODO'); | |
return { | |
id: action.id, | |
text: action.text, | |
completed: false | |
}; | |
case 'TOGGLE_TODO': | |
console.log('todo TOGGLE_TODO'); | |
// Bail out if the action is for a different todo than the one passed in. | |
if (state.id !== action.id) { | |
return state; | |
} | |
// ... below is ES7 "Object Rest Destructuring" | |
// https://github.com/sebmarkbage/ecmascript-rest-spread/blob/master/Spread.md | |
// Think of it as "and the rest", or "override these object properties" | |
return _extends({}, state, { | |
completed: !state.completed | |
}); | |
default: | |
return state; | |
} | |
}; | |
// 2nd level reducer | |
// Here, `state` refers to the array of todo objects | |
var todos = function todos(state, action) { | |
if (state === undefined) state = []; | |
console.log("todos reducer called"); | |
switch (action.type) { | |
// Returns a new array of todos, containing the added todo as described by `action`. | |
// The new todo is constructed by delegating to the `todo` reducer. | |
case 'ADD_TODO': | |
console.log('todos ADD_TODO'); | |
// ... below is ES6 "spread operator" (arrays only) | |
return [].concat(_toConsumableArray(state), [todo(undefined, action)]); | |
// Returns a new array of todos, with an individual todo's completed status | |
// toggled as identified by `action.id`. | |
// Must operate on entire list (seems wrong somehow). | |
case 'TOGGLE_TODO': | |
console.log('todos TOGGLE_TODO'); | |
return state.map(function (t) { | |
return todo(t, action); | |
}); | |
default: | |
return state; | |
} | |
}; | |
// 2nd level reducer | |
// Here, `state` refers to a simple configuration string (enum/atom) | |
// Remember, we are only returning the state we are concerned with | |
var visibilityFilter = function visibilityFilter(state, action) { | |
if (state === undefined) state = 'SHOW_ALL'; | |
console.log("visibilityFilter reducer called"); | |
switch (action.type) { | |
case 'SET_VISIBILITY_FILTER': | |
return action.filter; | |
default: | |
return state; | |
} | |
}; | |
// Top level reducer. | |
// combineReducers is not magic. | |
var _Redux = Redux; | |
var combineReducers = _Redux.combineReducers; | |
var todoApp = combineReducers({ | |
// n.b. following syntax is ES6 Object Initializer (shorthand property names) | |
todos: todos, // todos: todos, | |
visibilityFilter: visibilityFilter // visibilityFilter: visibilityFilter | |
}); | |
var _Redux2 = Redux; | |
var createStore = _Redux2.createStore; | |
var store = createStore(todoApp); | |
var _React = React; | |
var Component = _React.Component; | |
// This looks like just a function, not a React component. (Right?) | |
// No! This is a "stateless component". It gets `props` passed in. | |
// https://facebook.github.io/react/blog/2015/09/10/react-v0.14-rc1.html#stateless-function-components | |
// Notice the curlybraces in the parameter list. This is destructuring | |
// `props` (e.g. filter = props.filter, children = props.children) | |
var Link = function Link(_ref) { | |
var active = _ref.active; | |
var children = _ref.children; | |
var onClick = _ref.onClick; | |
if (active) return React.createElement( | |
'span', | |
null, | |
children | |
); | |
return React.createElement( | |
'a', | |
{ href: '#', | |
onClick: function (e) { | |
e.preventDefault(); | |
onClick(filter); | |
} | |
}, | |
children | |
); | |
}; | |
// Container component. Provides data and behavior for presentational components. | |
// When it mounts, it subscribes to store changes, allowing it to independently | |
// re-render. It delegates presnetation to the Link component. | |
var FilterLink = (function (_Component) { | |
_inherits(FilterLink, _Component); | |
function FilterLink() { | |
_classCallCheck(this, FilterLink); | |
_get(Object.getPrototypeOf(FilterLink.prototype), 'constructor', this).apply(this, arguments); | |
} | |
_createClass(FilterLink, [{ | |
key: 'componentDidMount', | |
value: function componentDidMount() { | |
var _this = this; | |
// n.b. Reads current state, but not subscribed. | |
// If parent component does not update when store is updated, will render stale value. | |
// Currently we re-render entire application when any state changes, but this is not efficient. | |
// We will instead move store subscription to the lifecycle methods of container copmonents. | |
// React provies a special forceUpdate method on component instances to force their rerendering. | |
this.unsubscribe = store.subscribe(function () { | |
_this.forceUpdate(); | |
}); | |
} | |
}, { | |
key: 'componentWillUnmount', | |
value: function componentWillUnmount() { | |
this.unsubscribe(); | |
} | |
}, { | |
key: 'render', | |
value: function render() { | |
var props = this.props; | |
var state = store.getState(); | |
return React.createElement( | |
Link, | |
{ | |
active: props.filter === state.visibilityFilter, | |
onClick: function () { | |
store.dispatch({ | |
type: 'SET_VISIBILITY_FILTER', | |
filter: props.filter | |
}); | |
} | |
}, | |
props.children | |
); | |
} | |
}]); | |
return FilterLink; | |
})(Component); | |
var AddTodo = function AddTodo(_ref2) { | |
var onAddClick = _ref2.onAddClick; | |
var input = undefined; | |
return React.createElement( | |
'div', | |
null, | |
React.createElement('input', { ref: function (node) { | |
// This is a newer 0.14 syntax where ref can be a callback. | |
// n.b. no `this` available in a functional/stateless component. | |
// Here, we're making a closure over `input`. | |
input = node; | |
} }), | |
React.createElement( | |
'button', | |
{ onClick: function () { | |
onAddClick(input.value); | |
input.value = ''; | |
} }, | |
'Add todo' | |
) | |
); | |
}; | |
var Footer = function Footer() { | |
return React.createElement( | |
'p', | |
null, | |
'Show:', | |
' ', | |
React.createElement( | |
FilterLink, | |
{ | |
filter: 'SHOW_ALL' }, | |
'All' | |
), | |
' ', | |
React.createElement( | |
FilterLink, | |
{ | |
filter: 'SHOW_ACTIVE' }, | |
'Active' | |
), | |
' ', | |
React.createElement( | |
FilterLink, | |
{ | |
filter: 'SHOW_COMPLETED' }, | |
'Completed' | |
) | |
); | |
}; | |
var getVisibleTodos = function getVisibleTodos(todos, filter) { | |
console.log('getVisibleTodos: todos=' + todos + ' filter=' + filter); | |
switch (filter) { | |
case 'SHOW_ALL': | |
return todos; | |
case 'SHOW_COMPLETED': | |
return todos.filter(function (t) { | |
return t.completed; | |
}); | |
case 'SHOW_ACTIVE': | |
return todos.filter(function (t) { | |
return !t.completed; | |
}); | |
} | |
}; | |
var nextToDoId = 0; // global :shrug: | |
var TodoApp = function TodoApp(_ref3) { | |
var todos = _ref3.todos; | |
var visibilityFilter = _ref3.visibilityFilter; | |
return React.createElement( | |
'div', | |
null, | |
React.createElement(AddTodo, { | |
onAddClick: function (text) { | |
store.dispatch({ | |
type: 'ADD_TODO', | |
id: nextToDoId++, | |
text: text | |
}); | |
} | |
}), | |
React.createElement(TodoList, { | |
todos: getVisibleTodos(todos, visibilityFilter), | |
onTodoClick: function (id) { | |
store.dispatch({ | |
type: 'TOGGLE_TODO', | |
id: id | |
}); | |
} | |
}), | |
React.createElement(Footer, null) | |
); | |
}; | |
var Todo = function Todo(_ref4) { | |
var onClick = _ref4.onClick; | |
var completed = _ref4.completed; | |
var text = _ref4.text; | |
return React.createElement( | |
'li', | |
{ | |
onClick: onClick, | |
style: { textDecoration: completed ? 'line-through' : 'none' | |
} | |
}, | |
text | |
); | |
}; | |
var TodoList = function TodoList(_ref5) { | |
var todos = _ref5.todos; | |
var onTodoClick = _ref5.onTodoClick; | |
return React.createElement( | |
'ul', | |
null, | |
todos.map(function (todo) { | |
return React.createElement(Todo, _extends({ | |
key: todo.id | |
}, todo, { | |
onClick: function () { | |
return onTodoClick(todo.id); | |
} | |
})); | |
}) | |
); | |
}; | |
var render = function render() { | |
console.log('render called'); | |
// JSX Spread Attributes. https://facebook.github.io/react/docs/jsx-spread.html | |
// <TodoApp {...store.getState()} /> | |
// is equivalent to: | |
// <ToDoApp | |
// todos={this.store.getState().props} | |
// visibilityFilter={this.store.getState().visibilityFilter} /> | |
ReactDOM.render(React.createElement(TodoApp, store.getState()), document.getElementById('root')); | |
}; | |
// Call render every time the store has an action dispatched | |
store.subscribe(render); | |
// Call render once for initial display | |
render(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment