App.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import React, { Component } from 'react';
  2. import './App.css';
  3. import { connect, Provider } from 'react-redux';
  4. import { createStore, combineReducers } from 'redux';
  5. function todoReducer(state, action){
  6. if (typeof state === 'undefined'){
  7. return {items: JSON.parse(localStorage.todo || "[]")};
  8. }
  9. if (action.type === 'NEW_TODO'){
  10. console.log(state);
  11. return {items: [...state.items, {title: action.title, text: action.text, timestamp: (new Date()).getTime()}]}
  12. }
  13. if (action.type === 'DELETE_TODO'){
  14. let items = [];
  15. for (let i=0;i<state.items.length;i++){
  16. if (state.items[i].timestamp !== action.timestamp){
  17. items.push(state.items[i]);
  18. }
  19. }
  20. return {items};
  21. }
  22. }
  23. const reducers = combineReducers({
  24. todo: todoReducer,
  25. })
  26. var store = createStore(reducers)
  27. store.subscribe(() => {
  28. let state = store.getState();
  29. localStorage.todo = JSON.stringify(state.todo.items);
  30. })
  31. class ToDoItem extends Component {
  32. constructor(props){
  33. super(props);
  34. this.delete = this.delete.bind(this);
  35. }
  36. delete() {
  37. store.dispatch({type: "DELETE_TODO", timestamp: this.props.item.timestamp})
  38. }
  39. render() {
  40. return (
  41. <div>
  42. <h3> { this.props.item.title } </h3>
  43. <p> { this.props.item.text }</p>
  44. <span> { (new Date(this.props.item.timestamp)).toLocaleString() } {this.props.item.timestamp} </span>
  45. <button onClick={this.delete} >x</button>
  46. </div>
  47. );
  48. }
  49. }
  50. class ToDoForm extends Component {
  51. constructor (props) {
  52. super(props);
  53. this.save = this.save.bind(this);
  54. this.state = {valid: true};
  55. }
  56. validator(){
  57. return this.title.value && this.text.value;
  58. }
  59. save() {
  60. let valid = this.validator();
  61. this.setState({valid})
  62. if (valid){
  63. store.dispatch({type: 'NEW_TODO',
  64. title: this.title.value,
  65. text: this.text.value})
  66. this.title.value = '';
  67. this.text.value = '';
  68. }
  69. }
  70. render (){
  71. let style = {
  72. backgroundColor: this.state.valid ? '' : 'red'
  73. }
  74. return (
  75. <div>
  76. <input style={style} placeholder='title' ref={ c => this.title = c}/>
  77. <input style={style} placeholder='text' ref={ c => this.text = c} />
  78. <button onClick = { this.save } >Save</button>
  79. </div>
  80. );
  81. }
  82. }
  83. class ToDoList extends Component {
  84. render (){
  85. return (
  86. <div> <h1>TODO LIST</h1>
  87. <ToDoForm />
  88. { this.props.items.map( (item, index) => <ToDoItem key={index} item={item} />) }
  89. </div>
  90. );
  91. }
  92. }
  93. const mapStateToProps = function(store){
  94. return {
  95. items: store.todo.items
  96. };
  97. }
  98. ToDoList = connect(mapStateToProps)(ToDoList);
  99. class App extends Component {
  100. render() {
  101. return (
  102. <div className="App">
  103. <Provider store={store} >
  104. <ToDoList />
  105. </Provider>
  106. </div>
  107. );
  108. }
  109. }
  110. export default App;