Last active
June 2, 2016 11:12
-
-
Save baurine/ea41651415710f2addc3d2b64b53a5ab to your computer and use it in GitHub Desktop.
redux_react_todo
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 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> |
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
// 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