Created
December 3, 2019 22:13
-
-
Save raydot/235bdbf1ab3336b2bd313d65e89e0df7 to your computer and use it in GitHub Desktop.
Redux Tutorial Video 23 -- Extracting Container Components Part 2 // source https://jsbin.com/neseqom
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> | |
<meta name="description" content="Video 23 -- Extracting Container Components Part 2"> | |
<meta charset="utf-8"> | |
<title>Redux Tutorial</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.4/redux.min.js"></script> | |
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> | |
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> | |
</head> | |
<body> | |
<div id='root'></div> | |
<script id="jsbin-javascript"> | |
// Let's finish separating the presentation and container components | |
// Don't take this as dogma. Only do this when it reduces complexity | |
'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) { | |
switch (action.type) { | |
case 'ADD_TODO': | |
return { | |
id: action.id, | |
text: action.text, | |
completed: false | |
}; | |
case 'TOGGLE_TODO': | |
if (state.id !== action.id) { | |
return state; | |
} | |
return _extends({}, state, { | |
completed: !state.completed | |
}); | |
default: | |
return state; | |
} | |
}; | |
// todos reducer | |
var todos = function todos(state, action) { | |
if (state === undefined) state = []; | |
switch (action.type) { | |
case 'ADD_TODO': | |
return [].concat(_toConsumableArray(state), [// return array with all items + new | |
todo(undefined, action) // calls child reducers | |
]); | |
case 'TOGGLE_TODO': | |
return state.map(function (t) { | |
return todo(t, action); | |
}); | |
default: | |
return state; | |
} | |
}; | |
var visibilityFilter = function visibilityFilter(state, action) { | |
if (state === undefined) state = 'SHOW_ALL'; | |
switch (action.type) { | |
case 'SET_VISIBILITY_FILTER': | |
return action.filter; | |
default: | |
return state; | |
} | |
}; | |
var _Redux = Redux; | |
var combineReducers = _Redux.combineReducers; | |
// This is the root reducer | |
var todoApp = combineReducers({ | |
todos: todos, | |
visibilityFilter: visibilityFilter | |
}); | |
var _Redux2 = Redux; | |
var createStore = _Redux2.createStore; | |
// combined reducer | |
var store = createStore(todoApp); | |
var _React = React; | |
var Component = _React.Component; | |
// react presentational component for utilizing visibility filter | |
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(); | |
} | |
}, | |
children | |
); | |
}; | |
// container to provide data and behavior to presentational component | |
// Renders linked based on props and Redux store state | |
var FilterLink = (function (_Component) { | |
_inherits(FilterLink, _Component); | |
function FilterLink() { | |
_classCallCheck(this, FilterLink); | |
_get(Object.getPrototypeOf(FilterLink.prototype), 'constructor', this).apply(this, arguments); | |
} | |
// Presentational component | |
_createClass(FilterLink, [{ | |
key: 'componentDidMount', | |
value: function componentDidMount() { | |
var _this = this; | |
// Unsubscribe() is returned by subscribe() | |
this.unsubscribe = store.subscribe(function () { | |
return _this.forceUpdate(); | |
}); | |
} | |
}, { | |
key: 'componentWillUnmount', | |
value: function componentWillUnmount() { | |
this.unsubscribe(); | |
} | |
}, { | |
key: 'render', | |
value: function render() { | |
var props = this.props; | |
var state = store.getState(); | |
// delegate rendering to presentational component. | |
return React.createElement( | |
Link, | |
{ | |
active: props.filter === state.visibilityFilter, | |
onClick: function () { | |
return store.dispatch({ | |
type: 'SET_VISIBILITY_FILTER', | |
filter: props.filter | |
}); | |
} | |
}, | |
props.children | |
); //return | |
} | |
}]); | |
return FilterLink; | |
})(Component); | |
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' | |
) | |
); | |
}; | |
// Purely presentational Todo component | |
var Todo = function Todo(_ref2) { | |
var onClick = _ref2.onClick; | |
var completed = _ref2.completed; | |
var text = _ref2.text; | |
var id = _ref2.id; | |
return React.createElement( | |
'li', | |
{ | |
onClick: onClick, | |
style: { | |
textDecoration: completed ? 'line-through' : 'none' | |
} | |
}, | |
id, | |
': ', | |
text | |
); | |
}; | |
// Purely presentational TodoList | |
var TodoList = function TodoList(_ref3) { | |
var todos = _ref3.todos; | |
var onTodoClick = _ref3.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); | |
} | |
})); | |
}) | |
); | |
}; | |
// Not really presentational or container | |
var AddTodo = function AddTodo(_ref4) { | |
var onAddClick = _ref4.onAddClick; | |
var input = undefined; | |
return React.createElement( | |
'div', | |
null, | |
React.createElement('input', { ref: function (node) { | |
input = node; | |
} }), | |
React.createElement( | |
'button', | |
{ onClick: function () { | |
store.dispatch({ | |
type: 'ADD_TODO', | |
id: nextTodoId++, | |
text: input.value | |
}); | |
input.value = ''; | |
} }, | |
'Add Todo' | |
) | |
); | |
}; | |
var getVisibleTodos = function getVisibleTodos(todos, 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; | |
}); | |
} | |
}; | |
// Container component. | |
// subscribes to store and rerenders todo list when store state changes | |
// calculates visible todos based on redux store state | |
// Rendering is performed by TodoList component. | |
var VisibleTodoList = (function (_Component2) { | |
_inherits(VisibleTodoList, _Component2); | |
function VisibleTodoList() { | |
_classCallCheck(this, VisibleTodoList); | |
_get(Object.getPrototypeOf(VisibleTodoList.prototype), 'constructor', this).apply(this, arguments); | |
} | |
_createClass(VisibleTodoList, [{ | |
key: 'componentDidMount', | |
value: function componentDidMount() { | |
var _this2 = this; | |
this.unsubscribe = store.subscribe(function () { | |
return _this2.forceUpdate(); | |
}); | |
} | |
}, { | |
key: 'componentWillUnmount', | |
value: function componentWillUnmount() { | |
this.unsubscribe(); | |
} | |
}, { | |
key: 'render', | |
value: function render() { | |
var props = this.props; | |
var state = store.getState(); | |
return React.createElement(TodoList, { | |
todos: getVisibleTodos(state.todos, state.visibilityFilter), | |
onTodoClick: function (id) { | |
return store.dispatch({ | |
type: 'TOGGLE_TODO', | |
id: id | |
}); | |
} | |
}); | |
} | |
}]); | |
return VisibleTodoList; | |
})(Component); | |
var nextTodoId = 0; | |
// With the refactor of TodoList, AddTodo, and Footer, TodoApp() this is now acting as a container component. | |
var TodoApp = function TodoApp() { | |
return React.createElement( | |
'div', | |
null, | |
React.createElement(AddTodo, null), | |
React.createElement(VisibleTodoList, null), | |
React.createElement(Footer, null) | |
); | |
}; | |
var render = function render() { | |
ReactDOM.render(React.createElement(TodoApp, null), document.getElementById('root')); | |
}; | |
store.subscribe(render); // runs on any state change | |
render(); | |
</script> | |
<script id="jsbin-source-javascript" type="text/javascript">// Let's finish separating the presentation and container components | |
// Don't take this as dogma. Only do this when it reduces complexity | |
const todo = ( state, action ) => { | |
switch (action.type) { | |
case 'ADD_TODO': | |
return { | |
id: action.id, | |
text: action.text, | |
completed: false | |
}; | |
case 'TOGGLE_TODO': | |
if (state.id !== action.id) { | |
return state; | |
} | |
return { | |
...state, | |
completed: !state.completed | |
} | |
default: | |
return state | |
} | |
} | |
// todos reducer | |
const todos = (state = [], action) => { | |
switch (action.type) { | |
case 'ADD_TODO': | |
return [ | |
...state, // return array with all items + new | |
todo(undefined, action) // calls child reducers | |
] | |
case 'TOGGLE_TODO': | |
return state.map(t => todo(t, action)) | |
default: | |
return state; | |
} | |
}; | |
const visibilityFilter = ( | |
state = 'SHOW_ALL', | |
action | |
) => { | |
switch (action.type) { | |
case 'SET_VISIBILITY_FILTER': | |
return action.filter; | |
default: | |
return state; | |
} | |
} | |
const { combineReducers } = Redux | |
// This is the root reducer | |
const todoApp = combineReducers({ | |
todos, | |
visibilityFilter | |
}) | |
const { createStore } = Redux | |
// combined reducer | |
const store = createStore(todoApp) | |
const { Component } = React | |
// react presentational component for utilizing visibility filter | |
const Link = ({ | |
active, | |
children, | |
onClick | |
}) => { | |
if (active) { | |
return <span>{children}</span>; | |
} | |
return ( | |
<a href='#' | |
onClick={e => { | |
e.preventDefault(); | |
onClick() | |
}} | |
> | |
{children} | |
</a> | |
); | |
}; | |
// container to provide data and behavior to presentational component | |
// Renders linked based on props and Redux store state | |
class FilterLink extends Component { | |
componentDidMount() { | |
// Unsubscribe() is returned by subscribe() | |
this.unsubscribe = store.subscribe(() => | |
this.forceUpdate() | |
); | |
} | |
componentWillUnmount() { | |
this.unsubscribe(); | |
} | |
render() { | |
const props = this.props; | |
const state = store.getState(); | |
// delegate rendering to presentational component. | |
return ( | |
<Link | |
active={ | |
props.filter === | |
state.visibilityFilter | |
} | |
onClick = {() => | |
store.dispatch({ | |
type: 'SET_VISIBILITY_FILTER', | |
filter: props.filter | |
}) | |
} | |
> | |
{props.children} | |
</Link> | |
); //return | |
} | |
} | |
// Presentational component | |
const Footer = () => ( | |
<p> | |
Show: | |
{' '} | |
<FilterLink | |
filter='SHOW_ALL' | |
> | |
All | |
</FilterLink> | |
{', '} | |
<FilterLink | |
filter='SHOW_ACTIVE' | |
> | |
Active | |
</FilterLink> | |
{', '} | |
<FilterLink | |
filter='SHOW_COMPLETED' | |
> | |
Completed | |
</FilterLink> | |
</p> | |
); | |
// Purely presentational Todo component | |
const Todo = ({ | |
onClick, | |
completed, | |
text, | |
id | |
}) => ( | |
<li | |
onClick={onClick} | |
style={{ | |
textDecoration: | |
completed ? | |
'line-through' : | |
'none' | |
}} | |
> | |
{id}: {text} | |
</li> | |
); | |
// Purely presentational TodoList | |
const TodoList = ({ | |
todos, | |
onTodoClick | |
}) => ( | |
<ul> | |
{todos.map(todo => | |
<Todo | |
key={todo.id} | |
{...todo} | |
onClick={() => onTodoClick(todo.id)} | |
/> | |
)} | |
</ul> | |
); | |
// Not really presentational or container | |
const AddTodo = ({ | |
onAddClick | |
}) => { | |
let input; | |
return ( | |
<div> | |
<input ref={node => { | |
input = node; | |
}} /> | |
<button onClick = {() => { | |
store.dispatch({ | |
type: 'ADD_TODO', | |
id: nextTodoId++, | |
text: input.value | |
}) | |
input.value = '' | |
}}> | |
Add Todo | |
</button> | |
</div> | |
); | |
}; | |
const getVisibleTodos = ( | |
todos, | |
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 | |
); | |
} | |
} | |
// Container component. | |
// subscribes to store and rerenders todo list when store state changes | |
// calculates visible todos based on redux store state | |
// Rendering is performed by TodoList component. | |
class VisibleTodoList extends Component { | |
componentDidMount() { | |
this.unsubscribe = store.subscribe(() => | |
this.forceUpdate() | |
); | |
} | |
componentWillUnmount() { | |
this.unsubscribe(); | |
} | |
render() { | |
const props = this.props; | |
const state = store.getState() | |
return ( | |
<TodoList | |
todos={ | |
getVisibleTodos( | |
state.todos, | |
state.visibilityFilter | |
) | |
} | |
onTodoClick={id => | |
store.dispatch({ | |
type: 'TOGGLE_TODO', | |
id | |
}) | |
} | |
/> | |
); | |
} | |
} | |
let nextTodoId = 0; | |
// With the refactor of TodoList, AddTodo, and Footer, TodoApp() this is now acting as a container component. | |
const TodoApp = () => ( | |
<div> | |
<AddTodo /> | |
<VisibleTodoList /> | |
<Footer /> | |
</div> | |
); | |
const render = () => { | |
ReactDOM.render( | |
<TodoApp />, | |
document.getElementById('root') | |
) | |
} | |
store.subscribe(render) // runs on any state change | |
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
// Let's finish separating the presentation and container components | |
// Don't take this as dogma. Only do this when it reduces complexity | |
'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) { | |
switch (action.type) { | |
case 'ADD_TODO': | |
return { | |
id: action.id, | |
text: action.text, | |
completed: false | |
}; | |
case 'TOGGLE_TODO': | |
if (state.id !== action.id) { | |
return state; | |
} | |
return _extends({}, state, { | |
completed: !state.completed | |
}); | |
default: | |
return state; | |
} | |
}; | |
// todos reducer | |
var todos = function todos(state, action) { | |
if (state === undefined) state = []; | |
switch (action.type) { | |
case 'ADD_TODO': | |
return [].concat(_toConsumableArray(state), [// return array with all items + new | |
todo(undefined, action) // calls child reducers | |
]); | |
case 'TOGGLE_TODO': | |
return state.map(function (t) { | |
return todo(t, action); | |
}); | |
default: | |
return state; | |
} | |
}; | |
var visibilityFilter = function visibilityFilter(state, action) { | |
if (state === undefined) state = 'SHOW_ALL'; | |
switch (action.type) { | |
case 'SET_VISIBILITY_FILTER': | |
return action.filter; | |
default: | |
return state; | |
} | |
}; | |
var _Redux = Redux; | |
var combineReducers = _Redux.combineReducers; | |
// This is the root reducer | |
var todoApp = combineReducers({ | |
todos: todos, | |
visibilityFilter: visibilityFilter | |
}); | |
var _Redux2 = Redux; | |
var createStore = _Redux2.createStore; | |
// combined reducer | |
var store = createStore(todoApp); | |
var _React = React; | |
var Component = _React.Component; | |
// react presentational component for utilizing visibility filter | |
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(); | |
} | |
}, | |
children | |
); | |
}; | |
// container to provide data and behavior to presentational component | |
// Renders linked based on props and Redux store state | |
var FilterLink = (function (_Component) { | |
_inherits(FilterLink, _Component); | |
function FilterLink() { | |
_classCallCheck(this, FilterLink); | |
_get(Object.getPrototypeOf(FilterLink.prototype), 'constructor', this).apply(this, arguments); | |
} | |
// Presentational component | |
_createClass(FilterLink, [{ | |
key: 'componentDidMount', | |
value: function componentDidMount() { | |
var _this = this; | |
// Unsubscribe() is returned by subscribe() | |
this.unsubscribe = store.subscribe(function () { | |
return _this.forceUpdate(); | |
}); | |
} | |
}, { | |
key: 'componentWillUnmount', | |
value: function componentWillUnmount() { | |
this.unsubscribe(); | |
} | |
}, { | |
key: 'render', | |
value: function render() { | |
var props = this.props; | |
var state = store.getState(); | |
// delegate rendering to presentational component. | |
return React.createElement( | |
Link, | |
{ | |
active: props.filter === state.visibilityFilter, | |
onClick: function () { | |
return store.dispatch({ | |
type: 'SET_VISIBILITY_FILTER', | |
filter: props.filter | |
}); | |
} | |
}, | |
props.children | |
); //return | |
} | |
}]); | |
return FilterLink; | |
})(Component); | |
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' | |
) | |
); | |
}; | |
// Purely presentational Todo component | |
var Todo = function Todo(_ref2) { | |
var onClick = _ref2.onClick; | |
var completed = _ref2.completed; | |
var text = _ref2.text; | |
var id = _ref2.id; | |
return React.createElement( | |
'li', | |
{ | |
onClick: onClick, | |
style: { | |
textDecoration: completed ? 'line-through' : 'none' | |
} | |
}, | |
id, | |
': ', | |
text | |
); | |
}; | |
// Purely presentational TodoList | |
var TodoList = function TodoList(_ref3) { | |
var todos = _ref3.todos; | |
var onTodoClick = _ref3.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); | |
} | |
})); | |
}) | |
); | |
}; | |
// Not really presentational or container | |
var AddTodo = function AddTodo(_ref4) { | |
var onAddClick = _ref4.onAddClick; | |
var input = undefined; | |
return React.createElement( | |
'div', | |
null, | |
React.createElement('input', { ref: function (node) { | |
input = node; | |
} }), | |
React.createElement( | |
'button', | |
{ onClick: function () { | |
store.dispatch({ | |
type: 'ADD_TODO', | |
id: nextTodoId++, | |
text: input.value | |
}); | |
input.value = ''; | |
} }, | |
'Add Todo' | |
) | |
); | |
}; | |
var getVisibleTodos = function getVisibleTodos(todos, 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; | |
}); | |
} | |
}; | |
// Container component. | |
// subscribes to store and rerenders todo list when store state changes | |
// calculates visible todos based on redux store state | |
// Rendering is performed by TodoList component. | |
var VisibleTodoList = (function (_Component2) { | |
_inherits(VisibleTodoList, _Component2); | |
function VisibleTodoList() { | |
_classCallCheck(this, VisibleTodoList); | |
_get(Object.getPrototypeOf(VisibleTodoList.prototype), 'constructor', this).apply(this, arguments); | |
} | |
_createClass(VisibleTodoList, [{ | |
key: 'componentDidMount', | |
value: function componentDidMount() { | |
var _this2 = this; | |
this.unsubscribe = store.subscribe(function () { | |
return _this2.forceUpdate(); | |
}); | |
} | |
}, { | |
key: 'componentWillUnmount', | |
value: function componentWillUnmount() { | |
this.unsubscribe(); | |
} | |
}, { | |
key: 'render', | |
value: function render() { | |
var props = this.props; | |
var state = store.getState(); | |
return React.createElement(TodoList, { | |
todos: getVisibleTodos(state.todos, state.visibilityFilter), | |
onTodoClick: function (id) { | |
return store.dispatch({ | |
type: 'TOGGLE_TODO', | |
id: id | |
}); | |
} | |
}); | |
} | |
}]); | |
return VisibleTodoList; | |
})(Component); | |
var nextTodoId = 0; | |
// With the refactor of TodoList, AddTodo, and Footer, TodoApp() this is now acting as a container component. | |
var TodoApp = function TodoApp() { | |
return React.createElement( | |
'div', | |
null, | |
React.createElement(AddTodo, null), | |
React.createElement(VisibleTodoList, null), | |
React.createElement(Footer, null) | |
); | |
}; | |
var render = function render() { | |
ReactDOM.render(React.createElement(TodoApp, null), document.getElementById('root')); | |
}; | |
store.subscribe(render); // runs on any state change | |
render(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment