examples/example-todolist.md

want a "TodoMVC Benchmarking" See example-performance

See also https://github.com/mweststrate/redux-todomvc and https://github.com/mweststrate/mobx-todomvc

code

<div id="react-content"></div>
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { set, remove } from 'immutable-data';
import 'todomvc-app-css/index.css'
import classnames from 'classnames';
import { createSelector } from 'reselect'
import Rcf from 'index.js';


let ID = 0;
const LIST = [];
const SIZE = 5;
for(let i=0;i < SIZE; i++) {
  LIST.push({
    text: 'task ' + i,
    completed: false,
    id: ID++,
  });
}

const store = {
  todolist: { 
    list: LIST,
    add(text, e) {
      return {
        list: [{
          text,
          completed: false,
          id: ID++,
        }, ...e.store.list],
      };
    },
    del(todo, e) {
      const index = e.store.list.indexOf(todo);
      return {
        list: remove(e.store.list, index),
      };
    },
    change(todo, e) {
      const index = e.store.list.indexOf(todo);
      return {
        list: set(e.store.list, {
          [`${index}.completed`]: !e.store.list[index].completed,
        }),
      };
    },
    edit(todo, text, e) {
      const index = e.store.list.indexOf(todo);
      return {
        list: set(e.store.list, {
          [`${index}.text`]: text,
        }),
      };
    },
    filter: 'all',
    changeFilter(filter) {
      return {
        filter,
      };
    },
    clearCompleted(e) {
      return {
        list: e.store.list.filter(item => !item.completed),
      };
    },
    toggle(e) {
      const activeTodosCount = getActiveTodosCount(e.store);
      if (activeTodosCount === 0 || e.store.list.length === activeTodosCount) {
        return {
          list: e.store.list.map(todo => ({
            ...todo,
            completed: !todo.completed,
          })),
        };
      }
      return {
        list: e.store.list.map(todo => todo.completed ? todo : {
          ...todo,
          completed: true,
        }),
      };
    }
  },
};

const getVisibleTodos = createSelector([
  store => store.list,
  store => store.filter,
], (list, filter) => {
  switch (filter) {
    case 'all':
      return list;
    case 'active':
      return list.filter(todo => !todo.completed);
    case 'completed':
      return list.filter(todo => todo.completed);
  }
});

const getActiveTodosCount = createSelector([
  store => store.list,
], list => {
  return list.filter(t => !t.completed).length;
});


const TodoList = ({ todolist }) => {
  const { change, del, add, filter, list, changeFilter, clearCompleted, toggle, edit } = todolist;
  const filterList = getVisibleTodos(todolist);
  const todoProps = { change, del, edit };
  const footerProps = { list, filter, changeFilter, clearCompleted };
  const toggleAllProps = { list, toggle };
  return <div className="todoapp">
    <header className="header">
      <h1>todos</h1>
    </header>
    <TodoInput
      type='new-todo'
      onSave={text => add(text)}
     />
    <div className="main">
      <ToggleAll {...toggleAllProps} />
      <ul className="todo-list">
        {
          filterList.map(todo =>
            <Todo key={todo.id} todo={todo} {...todoProps} />
          )
        }
      </ul>
      <Footer {...footerProps} />
    </div>
  </div>;
};


class Todo extends Component {
  constructor(props) {
    super(props)
    this.state = {
      type: 'text',
    };
  }
  handleDoubleClick = () => {
    this.setState({ 
      type: 'edit',
    });
  }
  handleSave = text => {
    this.props.edit(this.props.todo, text)
    this.setState({ 
      type: 'text',
    });
  }
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.todo !== this.props.todo || nextState.type !== this.state.type;
  }
  render() {
    const { todo, change, del, edit } = this.props;
    return <li className={classnames({
        completed: todo.completed,
        editing: this.state.type === 'edit',
      })}>

      {
        this.state.type === 'edit' ?

        <TodoInput
          text={todo.text}
          type="edit-todo"
          onSave={this.handleSave}
        />

        :

        <div className="view">
          <input
            className="toggle"
            type="checkbox"
            checked={todo.completed}
            onChange={() => change(todo)} />
          <label onDoubleClick={this.handleDoubleClick}>
            {todo.text}
          </label>
          <button className="destroy" onClick={() => del(todo)} />
        </div>

      }

    </li>;
  }

}


class TodoInput extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: '' || props.text,
    };
  }
  shouldComponentUpdate(nextProps, nextState) {
    return this.state.text !== nextState.text;
  }
  handleChange = e => {
    this.setState({
      text: e.target.value,
    });
  }
  handleSubmit = e => {
    e.preventDefault()
    const text = this.state.text;
    if (!text){
      return;
    }
    this.props.onSave(text);
    if (this.props.type === 'new-todo') {
      this.setState({
        text: '',
      });
    }
  }
  handleBlur = e => {
    if (this.props.type === 'edit-todo') {
      const text = this.state.text;
      this.props.onSave(text);
    }
  }
  render() {
    return <form onSubmit={this.handleSubmit}>
      <input type="text"
        className={classnames({
          'new-todo': this.props.type === 'new-todo',
          'edit': this.props.type === 'edit-todo',
        })}
        autoFocus="true"
        placeholder="What needs to be done?"
        value={this.state.text}
        onChange={this.handleChange}
        onBlur={this.handleBlur}
      />
    </form>;
  }
}


class Footer extends Component {
  render() {
    const { list, filter, changeFilter, clearCompleted } = this.props;
    const activeCount = getActiveTodosCount({ list });
    const completedCount = list.length - activeCount;
    return <footer className="footer">
      <span className="todo-count">
        <strong>{activeCount || 'No'}</strong>
        {' '}
        {activeCount === 1 ? 'item' : 'items'} left
      </span>
      <ul className="filters">
        {[ 'all', 'active', 'completed' ].map(item =>
          <li key={item}>
            <a className={classnames({ selected: item === filter })}
              style={{ cursor: 'pointer' }}
              onClick={() => changeFilter(item)}
            >
              {item}
            </a>
          </li>
        )}
      </ul>
      {
        completedCount > 0 
        &&
        <button className="clear-completed"
          onClick={() => clearCompleted()} >
          Clear completed
        </button>
      }
    </footer>;
  }
}


const ToggleAll = ({ list, toggle }) => {
  const completedCount = list.length - getActiveTodosCount({ list });
  return list.length > 0 ? <input
    className="toggle-all"
    type="checkbox"
    checked={completedCount === list.length}
    onChange={() => toggle()}
  /> : <span />;
}



ReactDOM.render(<Rcf store={store}>
  <TodoList />
</Rcf>,
document.getElementById('react-content'));
.wrapper {
  width: 100%;
}