Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  <script src="https://fb.me/react-15.1.0.js"></script>
  <script src="https://fb.me/react-dom-15.1.0.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/expect/1.20.2/expect.min.js"></script>
</head>
<body> 
  <div id="root"></div>
</body>
</html>
 
const todo = (state, action) => {
  switch (action.type) {
    case 'TOGGLE_TODO':
      if (state.id !== action.id) 
        return state;
      return {
        ...state,
        completed: !state.completed
      };
      
    case 'ADD_TODO':
      return action.payload;
    default:
      return state;
  }
}
const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        todo(undefined, action)
      ];
    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;
  }
}
let nextTodoId = 0;
const addTodo = (text) => {
  return {
    type: 'ADD_TODO',
    payload: {
      id: nextTodoId ++,
      text: text,
      completed: false
    }
  }
}
const { combineReducers } = Redux
const todoApp = combineReducers({
  todos, 
  visibilityFilter
})
const { Component } = React;
const Style = {
  complete: {
    textDecoration: 'line-through'
  }
};
const Link = ({
  children,
  active,
  onClick
}) => {
  if (active) {
    return <span>{children}</span>
  }
  return (
    <a href="#"
      onClick={onClick}
    >
      {children}
    </a>
  );
};
class FilterLink extends Component {
  componentDidMount() {
    const { store } = this.context;
    this.unsubscibe = store.subscribe(() => {
      this.forceUpdate();
    })
  }
  
  componentWillUnmount() {
    this.unsubscibe();
  }
  
  render() {
    const props = this.props;
    const { store } = this.context;
    const state = store.getState();
    
    return (
      <Link 
        active={
          props.filter === 
          state.visibilityFilter
        }
        onClick={() => {
          store.dispatch({
            type: 'SET_VISIBILITY_FILTER',
            filter: props.filter
          })
        }}
      >
        {props.children}
      </Link>
    )
  }
}
FilterLink.contextTypes = {
  store: React.PropTypes.object
}
const Todo = ({
  id,
  onClick,
  completed,
  text
}) => {
  return <li
    onClick={ onClick }
    style={ completed ? Style.complete : {} }
  >
    {text}
  </li>
}
const getVisiableTodos = (
  todos,
  visibilityFilter
) => {
  switch (visibilityFilter) {
    case 'SHOW_ALL':
      return todos;
    case 'SHOW_ACTIVE':
      return todos.filter(
        t => !t.completed
      );
    case 'SHOW_COMPLETED':
      return todos.filter(
        t => t.completed
      );
  }
}
const TodoList = ({
  todos,
  onTodoClick
}) => {
  return <ul>
    {todos.map(todo => 
     <Todo
       key={todo.id}
       {...todo} 
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
}
class VisiableTodoList extends Component {
  componentDidMount() {
    const { store } = this.context;
    this.unsubscribe = store.subscribe(() => {
      this.forceUpdate();
    })
  }
  
  componentWillUnmount() {
    this.unsubscribe();
  }
  
  render() {
    const props = this.props;
    const { store } = this.context;
    const state = store.getState();
    
    return (
      <TodoList
        todos={
          getVisiableTodos(
            state.todos,
            state.visibilityFilter
          )
        }
        onTodoClick={(id) => {
          store.dispatch({
            type: 'TOGGLE_TODO',
            id
          })
        }}
      />  
    )
  }
}
VisiableTodoList.contextTypes = {
  store: React.PropTypes.object
}
const AddTodo = (props, {store}) => {
  let input;
  return <div>
    <input 
      ref={node => {
        input = node;
      }}
    />
      <button 
        onClick={() => {
          if (input.value == '') return;
          store.dispatch(addTodo(input.value));
          input.value = '';
        }}
      >
        Add Todo
    </button>
  </div>        
}
AddTodo.contextTypes = {
  store: React.PropTypes.object
}
const Footer = () => {
  return <p>
    Show:
    {' '}
    <FilterLink filter="SHOW_ALL" >
      All
    </FilterLink>
    {' '}
    <FilterLink filter="SHOW_ACTIVE" >
      Active
    </FilterLink>
    {' '}
    <FilterLink filter="SHOW_COMPLETED" >
      Completed
    </FilterLink>
  </p>
}
const TodoApp = () =>  (
  <div>
    <AddTodo />
    <VisiableTodoList />
    <Footer />
  </div>
)
const { createStore } = Redux;
class Provider extends Component {
  
  getChildContext() {
    return {
      store: this.props.store
    }
  }
  
  render() {
    return this.props.children;
  }
}
Provider.childContextTypes = {
  store: React.PropTypes.object
}
ReactDOM.render(
  <Provider store={createStore(todoApp)}>
    <TodoApp />
  </Provider>,
  document.getElementById('root')
)
Output

This bin was created anonymously and its free preview time has expired (learn why). — Get a free unrestricted account

Dismiss x
public
Bin info
anonymouspro
0viewers