Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@baurine
Last active June 2, 2016 11:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save baurine/ea41651415710f2addc3d2b64b53a5ab to your computer and use it in GitHub Desktop.
Save baurine/ea41651415710f2addc3d2b64b53a5ab to your computer and use it in GitHub Desktop.
redux_react_todo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.0.4/redux.js"></script>
<script src="https://fb.me/react-0.14.7.min.js"></script>
<script src="https://fb.me/react-dom-0.14.7.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/4.0.0/react-redux.js"></script>
<body>
<div id="root"></div>
</body>
</html>
// works in http://jsbin.com
// select ES6/babel type
// lesson 24: https://egghead.io/lessons/javascript-redux-passing-the-store-down-explicitly-via-props
// Passing the Store down Explicity via Props
// lesson 25: https://egghead.io/lessons/javascript-redux-passing-the-store-down-implicitly-via-context
// Passing the Store down Implicity via Context
// demo how to implement Provider!
const todos = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
id: action.id,
text: action.text,
completed: false
}
];
case 'TOGGLE_TODO':
return state.map((todo) => {
if (todo.id != action.id) {
return todo;
}
return {
...todo,
completed: !todo.completed
}
});
default:
return state;
}
}
const visibilityFilter = (state = 'SHOW_ALL', action) => {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter;
default:
return state;
}
}
//////////////////////////////////
// extract action creator
let nextTodoId = 0;
const addTodo = (text) => {
return {
type: 'ADD_TODO',
id: nextTodoId++,
text
}
};
const toggleTodo = (id) => {
return {
type: 'TOGGLE_TODO',
id
}
};
const setVisibilityFilter = (filter) => {
return {
type: 'SET_VISIBILITY_FILTER',
filter
}
};
//////////////////////////////////
const { combineReducers } = Redux;
const todoApp = combineReducers({
todos,
visibilityFilter
});
//////////////////////////////////
const { Component } = React;
const TodoItem = ({completed, text, onClick}) => (
<li
onClick={onClick}
style={{
textDecoration: completed ?
'line-through':'none'
}}>
{text}
</li>
);
const TodoList = ({todos, onTodoClick}) => (
<ul>
{
todos.map( todo =>
<TodoItem
key={todo.id}
{...todo}
onClick={()=>onTodoClick(todo.id)}
/>
)
}
</ul>
);
/*
class VisibleTodoList extends Component {
componentDidMount() {
const { store } = this.context;
this.unsubscrible = store.subscribe(()=>{
this.forceUpdate();
});
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const props = this.props;
const { store } = this.context;
const state = store.getState();
return (
<TodoList
todos={getFilterTodos(state.todos, state.visibilityFilter)}
onTodoClick={ id => {
store.dispatch({
type: 'TOGGLE_TODO',
id
});
}}
/>
);
}
}
VisibleTodoList.contextTypes = {
store: React.PropTypes.object
};
*/
// use react-redux connect to implement the container component
// Strong Code Abstract Ability!
const mapStateToTodoListProps = (state) => {
return {
todos: getFilterTodos(
state.todos,
state.visibilityFilter)
}
}
const mapDispatchToTodoProps = (dispatch) => {
return {
onTodoClick: id => { dispatch(toggleTodo(id)) }
}
}
const { connect } = ReactRedux;
const VisibleTodoList = connect(
mapStateToTodoListProps,
mapDispatchToTodoProps
)(TodoList);
// extract AddTodo
// first param is props
// second param is context
// const AddTodo = (props, context) => {}
let AddTodo = ({dispatch}) => {
let input;
// console.log(store);
return (
<div>
<input type='input' ref={node=>input=node}/>
<button onClick={()=>{
dispatch(addTodo(input.value));
input.value = '';
}}>
Add Todo
</button>
</div>
);
};
// following all work
// 1.
/*
AddTodo = connect(
state=>{return {}},
dispatch=>{return {dispatch}}
)(AddTodo);
*/
// 2.
// AddTodo= connect(null,null)(AddTodo);
// 3.
AddTodo = connect()(AddTodo);
// extract FilterLink
// https://egghead.io/lessons/javascript-redux-extracting-container-components-filterlink
// here demo how Redux Connect implement
// Link is only used to display
// FilterLink is Link's container and controller
// it connect the Link with store
// it is not used to display, it is used to control logic
// we can use Redux connect method to replace the implemention of FilterLink
// like this: const FilterLink = connect(Link)
const Link = ({active, onClick, children}) => {
if (active) {
return (
<span>{children}</span>
);
}
return (
<a href="#"
onClick={(e)=>{
e.preventDefault();
onClick();
}}>
{children}
</a>
);
};
// notice, FilterLink is a component used to connect real component to Store
// so I call it connection component
// and author call it Container Component
/*
class FilterLink extends Component {
componentDidMount() {
const { store } = this.context;
// console.log(store);
this.unsubscrible = store.subscribe(()=>{
this.forceUpdate();
});
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const props = this.props;
const { store } = this.context;
const state = store.getState();
const active = props.filter === state.visibilityFilter;
return (
<Link
active={active}
onClick={()=>{
store.dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: props.filter
});
}}>
{props.children}
</Link>
);
}
}
FilterLink.contextTypes = {
store: React.PropTypes.object
};
*/
const mapStateToFilterLinkProps = (
state,
ownProps
) => {
return {
active: ownProps.filter === state.visibilityFilter
}
};
const mapDispatchToFilterLinkProps = (
dispatch,
ownProps
) => {
return {
onClick: ()=>{
dispatch(setVisibilityFilter(ownProps.filter));
}
}
};
const FilterLink = connect(
mapStateToFilterLinkProps,
mapDispatchToFilterLinkProps
)(Link);
// extract Footer
const Footer = () => (
<p>
Show:
{' '}
<FilterLink
filter='SHOW_ALL'>
All
</FilterLink>
{' '}
<FilterLink
filter='SHOW_ACTIVE'>
Active
</FilterLink>
{' '}
<FilterLink
filter='SHOW_COMPLETED'>
Completed
</FilterLink>
</p>
);
/////////////////////////////////////////
const getFilterTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_ALL':
return todos;
case 'SHOW_ACTIVE':
return todos.filter((t)=>!t.completed);
case 'SHOW_COMPLETED':
return todos.filter((t)=>t.completed);
}
}
/////////////////////////////////////////
const TodoApp = () => (
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
);
/////////////////////////////////////////
/*
class Provider extends Component {
getChildContext() {
return { store: this.props.store }
}
render() {
return this.props.children;
}
}
Provider.childContextTypes = {
store: React.PropTypes.object
};
*/
// use 'react-redux' Provider
const { Provider } = ReactRedux;
/////////////////////////////////////////
const { createStore } = Redux;
ReactDOM.render(
<Provider store={createStore(todoApp)}>
<TodoApp />
</Provider>,
document.getElementById('root')
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment