Created
December 3, 2019 20:14
-
-
Save raydot/57eb9d0d53c783de97ed272bb00cf603 to your computer and use it in GitHub Desktop.
Redux Tutorial Video 21 -- Extracting Presentational Components // 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 21 -- Extracting Presentational Components"> | |
<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"> | |
// Extracting and refactoring. | |
// Renders a single list item | |
'use strict'; | |
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); } } | |
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 component for utilizing visibility filter | |
var FilterLink = function FilterLink(_ref) { | |
var filter = _ref.filter; | |
var currentFilter = _ref.currentFilter; | |
var children = _ref.children; | |
var onClick = _ref.onClick; | |
if (filter === currentFilter) { | |
return React.createElement( | |
'span', | |
null, | |
children | |
); | |
} | |
return React.createElement( | |
'a', | |
{ href: '#', | |
onClick: function (e) { | |
e.preventDefault(); | |
onClick(filter); | |
} | |
}, | |
children | |
); | |
}; | |
var Footer = function Footer(_ref2) { | |
var visibilityFilter = _ref2.visibilityFilter; | |
var onFilterClick = _ref2.onFilterClick; | |
return React.createElement( | |
'p', | |
null, | |
'Show:', | |
' ', | |
React.createElement( | |
FilterLink, | |
{ | |
filter: 'SHOW_ALL', | |
currentFilter: visibilityFilter, | |
onClick: onFilterClick | |
}, | |
'All' | |
), | |
', ', | |
React.createElement( | |
FilterLink, | |
{ | |
filter: 'SHOW_ACTIVE', | |
currentFilter: visibilityFilter, | |
onClick: onFilterClick | |
}, | |
'Active' | |
), | |
', ', | |
React.createElement( | |
FilterLink, | |
{ | |
filter: 'SHOW_COMPLETED', | |
currentFilter: visibilityFilter, | |
onClick: onFilterClick | |
}, | |
'Completed' | |
) | |
); | |
}; | |
// Purely presentational Todo component | |
var Todo = function Todo(_ref3) { | |
var onClick = _ref3.onClick; | |
var completed = _ref3.completed; | |
var text = _ref3.text; | |
var id = _ref3.id; | |
return React.createElement( | |
'li', | |
{ | |
onClick: onClick, | |
style: { | |
textDecoration: completed ? 'line-through' : 'none' | |
} | |
}, | |
id, | |
': ', | |
text | |
); | |
}; | |
// Purely presentational TodoList | |
var TodoList = function TodoList(_ref4) { | |
var todos = _ref4.todos; | |
var onTodoClick = _ref4.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); | |
} | |
})); | |
}) | |
); | |
}; | |
// Purely presentational AddTodo | |
var AddTodo = function AddTodo(_ref5) { | |
var onAddClick = _ref5.onAddClick; | |
var input = undefined; | |
return React.createElement( | |
'div', | |
null, | |
React.createElement('input', { ref: function (node) { | |
input = node; | |
} }), | |
React.createElement( | |
'button', | |
{ onClick: function () { | |
onAddClick(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; | |
}); | |
} | |
}; | |
var nextTodoId = 0; | |
// With the refactor of TodoList, AddTodo, and Footer, TodoApp() this is now acting as a container component. | |
var TodoApp = function TodoApp(_ref6) { | |
var todos = _ref6.todos; | |
var visibilityFilter = _ref6.visibilityFilter; | |
return React.createElement( | |
'div', | |
null, | |
React.createElement(AddTodo, { | |
onAddClick: function (text) { | |
return store.dispatch({ | |
type: 'ADD_TODO', | |
id: nextTodoId++, | |
text: text | |
}); | |
} | |
}), | |
React.createElement(TodoList, { | |
todos: getVisibleTodos(todos, visibilityFilter), | |
onTodoClick: function (id) { | |
return store.dispatch({ | |
type: 'TOGGLE_TODO', | |
id: id | |
}); | |
} | |
}), | |
React.createElement(Footer, { | |
visibilityFilter: visibilityFilter, | |
onFilterClick: function (filter) { | |
return store.dispatch({ | |
type: 'SET_VISIBILITY_FILTER', | |
filter: filter | |
}); | |
} | |
}) | |
); | |
}; | |
var render = function render() { | |
ReactDOM.render( | |
// passes to component as prop, every state filled in inside the state object. | |
React.createElement(TodoApp, store.getState()), document.getElementById('root')); | |
}; | |
store.subscribe(render); // runs on any state change | |
render(); | |
</script> | |
<script id="jsbin-source-javascript" type="text/javascript">// Extracting and refactoring. | |
// Renders a single list item | |
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 component for utilizing visibility filter | |
const FilterLink = ({ | |
filter, | |
currentFilter, | |
children, | |
onClick | |
}) => { | |
if (filter === currentFilter) { | |
return ( | |
<span>{children}</span> | |
); | |
} | |
return ( | |
<a href='#' | |
onClick={e => { | |
e.preventDefault(); | |
onClick(filter) | |
}} | |
> | |
{children} | |
</a> | |
) | |
} | |
const Footer = ({ | |
visibilityFilter, | |
onFilterClick | |
}) => ( | |
<p> | |
Show: | |
{' '} | |
<FilterLink | |
filter='SHOW_ALL' | |
currentFilter={visibilityFilter} | |
onClick={onFilterClick} | |
> | |
All | |
</FilterLink> | |
{', '} | |
<FilterLink | |
filter='SHOW_ACTIVE' | |
currentFilter={visibilityFilter} | |
onClick={onFilterClick} | |
> | |
Active | |
</FilterLink> | |
{', '} | |
<FilterLink | |
filter='SHOW_COMPLETED' | |
currentFilter={visibilityFilter} | |
onClick={onFilterClick} | |
> | |
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> | |
); | |
// Purely presentational AddTodo | |
const AddTodo = ({ | |
onAddClick | |
}) => { | |
let input; | |
return ( | |
<div> | |
<input ref={node => { | |
input = node; | |
}} /> | |
<button onClick = {() => { | |
onAddClick(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 | |
); | |
} | |
} | |
let nextTodoId = 0; | |
// With the refactor of TodoList, AddTodo, and Footer, TodoApp() this is now acting as a container component. | |
const TodoApp =({ | |
todos, | |
visibilityFilter | |
}) => ( | |
<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 | |
visibilityFilter={visibilityFilter} | |
onFilterClick={filter => | |
store.dispatch({ | |
type: 'SET_VISIBILITY_FILTER', | |
filter | |
}) | |
} | |
/> | |
</div> | |
); | |
const render = () => { | |
ReactDOM.render( | |
// passes to component as prop, every state filled in inside the state object. | |
<TodoApp | |
{...store.getState()} | |
/>, | |
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
// Extracting and refactoring. | |
// Renders a single list item | |
'use strict'; | |
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); } } | |
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 component for utilizing visibility filter | |
var FilterLink = function FilterLink(_ref) { | |
var filter = _ref.filter; | |
var currentFilter = _ref.currentFilter; | |
var children = _ref.children; | |
var onClick = _ref.onClick; | |
if (filter === currentFilter) { | |
return React.createElement( | |
'span', | |
null, | |
children | |
); | |
} | |
return React.createElement( | |
'a', | |
{ href: '#', | |
onClick: function (e) { | |
e.preventDefault(); | |
onClick(filter); | |
} | |
}, | |
children | |
); | |
}; | |
var Footer = function Footer(_ref2) { | |
var visibilityFilter = _ref2.visibilityFilter; | |
var onFilterClick = _ref2.onFilterClick; | |
return React.createElement( | |
'p', | |
null, | |
'Show:', | |
' ', | |
React.createElement( | |
FilterLink, | |
{ | |
filter: 'SHOW_ALL', | |
currentFilter: visibilityFilter, | |
onClick: onFilterClick | |
}, | |
'All' | |
), | |
', ', | |
React.createElement( | |
FilterLink, | |
{ | |
filter: 'SHOW_ACTIVE', | |
currentFilter: visibilityFilter, | |
onClick: onFilterClick | |
}, | |
'Active' | |
), | |
', ', | |
React.createElement( | |
FilterLink, | |
{ | |
filter: 'SHOW_COMPLETED', | |
currentFilter: visibilityFilter, | |
onClick: onFilterClick | |
}, | |
'Completed' | |
) | |
); | |
}; | |
// Purely presentational Todo component | |
var Todo = function Todo(_ref3) { | |
var onClick = _ref3.onClick; | |
var completed = _ref3.completed; | |
var text = _ref3.text; | |
var id = _ref3.id; | |
return React.createElement( | |
'li', | |
{ | |
onClick: onClick, | |
style: { | |
textDecoration: completed ? 'line-through' : 'none' | |
} | |
}, | |
id, | |
': ', | |
text | |
); | |
}; | |
// Purely presentational TodoList | |
var TodoList = function TodoList(_ref4) { | |
var todos = _ref4.todos; | |
var onTodoClick = _ref4.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); | |
} | |
})); | |
}) | |
); | |
}; | |
// Purely presentational AddTodo | |
var AddTodo = function AddTodo(_ref5) { | |
var onAddClick = _ref5.onAddClick; | |
var input = undefined; | |
return React.createElement( | |
'div', | |
null, | |
React.createElement('input', { ref: function (node) { | |
input = node; | |
} }), | |
React.createElement( | |
'button', | |
{ onClick: function () { | |
onAddClick(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; | |
}); | |
} | |
}; | |
var nextTodoId = 0; | |
// With the refactor of TodoList, AddTodo, and Footer, TodoApp() this is now acting as a container component. | |
var TodoApp = function TodoApp(_ref6) { | |
var todos = _ref6.todos; | |
var visibilityFilter = _ref6.visibilityFilter; | |
return React.createElement( | |
'div', | |
null, | |
React.createElement(AddTodo, { | |
onAddClick: function (text) { | |
return store.dispatch({ | |
type: 'ADD_TODO', | |
id: nextTodoId++, | |
text: text | |
}); | |
} | |
}), | |
React.createElement(TodoList, { | |
todos: getVisibleTodos(todos, visibilityFilter), | |
onTodoClick: function (id) { | |
return store.dispatch({ | |
type: 'TOGGLE_TODO', | |
id: id | |
}); | |
} | |
}), | |
React.createElement(Footer, { | |
visibilityFilter: visibilityFilter, | |
onFilterClick: function (filter) { | |
return store.dispatch({ | |
type: 'SET_VISIBILITY_FILTER', | |
filter: filter | |
}); | |
} | |
}) | |
); | |
}; | |
var render = function render() { | |
ReactDOM.render( | |
// passes to component as prop, every state filled in inside the state object. | |
React.createElement(TodoApp, store.getState()), 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