<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Plunker</title>
<script src="https://unpkg.com/react@16.3.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.3.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
<script src="https://unpkg.com/redux@4.0.0/dist/redux.js"></script>
<script src="https://unpkg.com/react-redux@5.0.7/dist/react-redux.js"></script>
</head>
<body>
<div id='root'></div>
<script type="text/jsx" src="script.jsx"></script>
</body>
</html>
/*
* We will be explaining the code below
* in the following lessons. For now,
* feel free to click around and notice
* how the previous state tree, the
* dispatched action, and the next
* state tree are logged to the console
* on every change.
*/
const ADD_TODO = 'ADD_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';
const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';
const Filters = {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLETED: 'SHOW_COMPLETED',
SHOW_ACTIVE: 'SHOW_ACTIVE'
};
/*
* Components
*/
class AddTodo extends React.Component {
render() {
return (
<div>
<input type='text' ref='input' />
<button onClick={(e) => this.handleClick(e)}>
Add
</button>
</div>
);
}
handleClick(e) {
const node = this.refs.input;
const text = node.value.trim();
this.props.onAddClick(text);
node.value = '';
}
}
const FilterLink = ({ isActive, name, onClick }) => {
if (isActive) {
return <span>{name}</span>;
}
return (
<a href='#' onClick={e => { e.preventDefault(); onClick(); }}>
{name}
</a>
);
};
const Footer = ({ filter, onFilterChange }) => (
<p>
Show:
{' '}
<FilterLink
name='All'
isActive={filter === Filters.SHOW_ALL}
onClick={() => onFilterChange(Filters.SHOW_ALL)} />
{', '}
<FilterLink
name='Completed'
isActive={filter === Filters.SHOW_COMPLETED}
onClick={() => onFilterChange(Filters.SHOW_COMPLETED)} />
{', '}
<FilterLink
name='Active'
isActive={filter === Filters.SHOW_ACTIVE}
onClick={() => onFilterChange(Filters.SHOW_ACTIVE)} />
</p>
);
const Todo = ({ onClick, completed, text }) => (
<li
onClick={onClick}
style={{
textDecoration: completed ? 'line-through' : 'none',
cursor: 'pointer'
}}>
{text}
</li>
);
const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo {...todo}
key={todo.id}
onClick={() => onTodoClick(todo.id)} />
)}
</ul>
);
let nextTodoId = 0;
const TodoApp = ({ dispatch, todos, visibilityFilter }) => {
let visibleTodos = todos;
switch (visibilityFilter) {
case Filters.SHOW_COMPLETED:
visibleTodos = todos.filter(todo => todo.completed);
break;
case Filters.SHOW_ACTIVE:
visibleTodos = todos.filter(todo => !todo.completed);
break;
}
return (
<div>
<AddTodo
onAddClick={text =>
dispatch({ type: ADD_TODO, text, id: nextTodoId++ })
} />
<TodoList
todos={visibleTodos}
onTodoClick={id =>
dispatch({ type: TOGGLE_TODO, id })
} />
<Footer
filter={visibilityFilter}
onFilterChange={filter =>
dispatch({ type: SET_VISIBILITY_FILTER, filter })
} />
</div>
);
};
/*
* Reducers
*/
const visibilityFilter = (state = Filters.SHOW_ALL, action) => {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return action.filter;
default:
return state;
}
}
const todos = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [...state, {
text: action.text,
id: action.id,
completed: false
}];
case TOGGLE_TODO:
return state.map(todo =>
todo.id === action.id ?
Object.assign({}, todo, { completed: !todo.completed }) :
todo
);
default:
return state;
}
}
const todoApp = Redux.combineReducers({
visibilityFilter,
todos
});
/*
* Go!
*/
const store = Redux.createStore(todoApp);
const dispatch = (action) => {
console.log('----------------') || displayInPreview('----------------');
console.log('previous state:') || displayInPreview('previous state:');
console.log(store.getState()) || displayInPreview(store.getState());
console.log('dispatching action:') || displayInPreview('dispatching action:');
console.log(action) || displayInPreview(action);
store.dispatch(action) || displayInPreview(action);
console.log('next state:') || displayInPreview('next state:');
console.log(store.getState()) || displayInPreview(store.getState());
};
const render = () => {
ReactDOM.render(
<TodoApp
{...store.getState()}
dispatch={dispatch}
/>,
document.getElementById('root')
);
}
render();
store.subscribe(render);
console.log('initial state:') || displayInPreview('initial state:');
console.log(store.getState()) || displayInPreview(store.getState().visibilityFilter);
// noprotect
// display in plunker preview
function displayInPreview(string) {
var newDiv = document.createElement("div");
var newContent = document.createTextNode(string);
newDiv.appendChild(newContent);
document.body.appendChild(newDiv)
}