• ToDoList项目


    react hooks版本

    父子传值
    在这里插入图片描述

    header组件与input组件的编写

    openInput事件:Header子组件通知父组件App控制AddInput子组件

    Header.js

    import React from 'react';
    
    import './index.scss';
    
    function Header (props) {
      
      const { openInput } = props;
    
      return (
        <div className="header">
          <h1>事件待办</h1>
          <span className="icon" onClick={openInput}>
            &#43;
          </span>
        </div>
      );
    }
    
    export default Header;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    App.js

    import React, { useState } from 'react';
    
    import './App.scss';
    
    import MyHeader from './components/Header';
    functoin App() {
    const [ isShowInput, setIsShowInput ] = useState(false)
    
    return (
    	<div className="App">
          <MyHeader openInput={() => setIsShowInput(!isShowInput)} 		/>
    	</div>
    );
    
    }
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    AddInput.js

    import React from 'react';
    
    import './index.scss';
    
    function AddInput (props) {
      const { isShow } = props,
      return (
        <>
          {isShow && (
            <div className="input-wrapper">
              <input type="text"  placeholder="请输入待办事件" />
              <button className="btn btn-primary">
                增加
              </button>
            </div>
          )}
        </>
      );
    }
    
    export default AddInput;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    输入增加待办事项

    usecallback
    AddInput子组件将输入的数据传递给父组件App,App将数据存起来

    import React, { useCallback, useRef } from 'react';
    
    import './index.scss';
    
    function AddInput (props) {
      const { isShow, addItem } = props,
            inputRef = useRef();
    
      const submitValue = useCallback(
        () => {
          const inputValue = inputRef.current.value.trim();
          if (inputValue.length === 0) {
            return;
          }
    
          addItem(inputValue);
          inputRef.current.value = '';
        },
        [addItem]
      );
    
      return (
        <>
          {isShow && (
            <div className="input-wrapper">
              <input type="text" ref={inputRef} placeholder="请输入待办事件" />
              <button className="btn btn-primary" onClick={submitValue}>
                增加
              </button>
            </div>
          )}
        </>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    App.js

    import React, { useState, useEffect, useCallback } from 'react';
    
    import './App.scss';
    
    import MyHeader from './components/Header';
    import AddInput from './components/AddInput';
    import TodoItem from './components/TodoItem';
    
    function App() {
      
      const [ isShowInput, setIsShowInput ] = useState(false),
            [ todoList, setTodoList ] = useState([])
            
      const addItem = useCallback(
        (value) => {
          const dataItem = {
            id: new Date().getTime(),
            content: value,
            completed: false,
          };
          setTodoList((todoList) => [...todoList, dataItem]);
          setIsShowInput(false);
        },
        []
      );
    
      return (
        <div className="App">
          <MyHeader openInput={() => setIsShowInput(!isShowInput)} />
          <AddInput isShow={isShowInput} addItem={(value) => addItem(value)} />
          )}
        </div>
      );
    }
    
    export default App;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    遍历数据循环渲染ToDo列表

    App将数据其传递给TODOItem子组件进行显示
    App.js

    import React, { useState, useEffect, useCallback } from 'react';
    
    import './App.scss';
    
    import MyHeader from './components/Header';
    import AddInput from './components/AddInput';
    import TodoItem from './components/TodoItem';
    
    function App() {
      
      const [ isShowInput, setIsShowInput ] = useState(false),
            [ todoList, setTodoList ] = useState([])
      
      const addItem = useCallback(
        (value) => {
          const dataItem = {
            id: new Date().getTime(),
            content: value,
            completed: false,
          };
          setTodoList((todoList) => [...todoList, dataItem]);
          setIsShowInput(false);
        },
        []
      );
    
      return (
        <div className="App">
          <MyHeader openInput={() => setIsShowInput(!isShowInput)} />
          <AddInput isShow={isShowInput} addItem={(value) => addItem(value)} />
            <ul className="todo-list">
              {todoList.map((item, index) => {
                return (
                  <TodoItem
                    dataItem={item}
                    key={index}
                  />
                );
              })}
            </ul>
          )}
        </div>
      );
    }
    
    export default App;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    TodoItem,js

    import React from 'react';
    
    import './index.scss';
    
    function TodoItem (props) {
      const {
        dataItem,
      } = props;
    
      return (
        <li className="todo-item">
          <div className="check-box">
            <input
              type="checkbox"
              checked={dataItem.completed}
            />
          </div>
          <span
            className="content"
            style={{ textDecoration: dataItem.completed ? "line-through" : "none" }}
          >
            {dataItem.content}
          </span>
          <div className="btn-group">
            <button
              className="btn btn-primary"
            >
              查看
            </button>
            <button
              className="btn btn-warning"
            >
              编辑
            </button>
            <button
              className="btn btn-danger"
            >
              删除
            </button>
          </div>
        </li>
      );
    }
    
    export default TodoItem;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    数据存储到localStorage

    useeffct用法
    App.js

      useEffect(() => {
        const todoData = JSON.parse(localStorage.getItem("todoData") || '[]');
        setTodoList(todoData);
      }, []);
    
      useEffect(() => {
        localStorage.setItem("todoData", JSON.stringify(todoList));
      }, [todoList]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    模态框组件与插槽

    在这里插入图片描述
    在这里插入图片描述

    基础容器Model的插槽中添加一定的内容,构成了CheckModel和EditModel组件。

    Model.js

    import React from 'react';
    
    import '../../assets/css/common.scss';
    
    function Modal (props) {
      const { isShowModal, modalTitle, children } = props;
    
      return (
        <>
          {isShowModal ? (
            <div className="modal">
              <div className="inner">
                <div className="m-header">{modalTitle}</div>
                <div className="content-wrapper">{children}</div>
              </div>
            </div>
          ) : (
            ""
          )}
        </>
      );
    }
    
    export default Modal;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    CheckModal.js

    import React from 'react';
    
    import './index.scss';
    
    import Modal from '../';
    import { formatDateTime } from '../../../libs/utils';
    
    function CheckModal (props) {
      const { isShowCheckModal, data, closeModal } = props;
    
      return (
        <Modal isShowModal={isShowCheckModal} modalTitle="查看事件">
          <p className="topic">时间:{formatDateTime(data.id)}</p>
          <p className="topic">内容:{ data.content }</p>
          <p className="topic">状态:{ data.completed ? '已完成' : '未完成' }</p>
          <button 
            className="btn btn-primary comfirm-btn"
            onClick={ closeModal }
          >确定</button>
        </Modal>
      );
    }
    
    export default CheckModal;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    EditModal.js

    import React, { useRef, useCallback } from 'react';
    
    import './index.scss';
    
    import Modal from '../';
    import { formatDateTime } from '../../../libs/utils';
    
    function EditModal (props) {
      const { isShowEditModal, data, submitEdit } = props,
            inputRef = useRef(),
            checkRef = useRef();
      
      const formatNewData = useCallback(() => {
    
        const inputLen = inputRef.current.value.trim().length;
    
        if (inputLen === 0) {
          inputRef.current.value = data.content;
          return;
        }
    
        const newData = {
          id: new Date().getTime(),
          content: inputRef.current.value,
          completed: checkRef.current.checked,
        };
    
        submitEdit(newData, data.id);
      }, [submitEdit, data]);
    
      return (
        <Modal isShowModal={isShowEditModal} modalTitle="编辑事件">
          <p className="topic">时间:{formatDateTime(data.id)}</p>
          <p className="topic">
            <textarea
              ref={inputRef}
              defaultValue={data.content}
              className="text-area"
            ></textarea>
          </p>
          <p className="topic">
            状态:
            <input
              type="checkbox"
              defaultChecked={data.completed ? true : false}
              ref={checkRef}
            />
          </p>
          <button className="btn btn-primary comfirm-btn" onClick={formatNewData}>
            确定
          </button>
        </Modal>
      );
    }
    
    export default EditModal;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    App.js

    import React, { useState, useEffect, useCallback } from 'react';
    
    import './App.scss';
    
    import MyHeader from './components/Header';
    import AddInput from './components/AddInput';
    import NoDataTip from './components/NoDataTip';
    import TodoItem from './components/TodoItem';
    import CheckModal from './components/Modal/CheckModal';
    import EditModal from './components/Modal/EditModal';
    
    function App() {
      
      const [ isShowInput, setIsShowInput ] = useState(false),
            [ isShowCheckModal, setIsShowCheckModal ] = useState(false),
            [ isShowEditModal, setIsShowEditModal ] = useState(false),
            [ todoList, setTodoList ] = useState([]),
            [ currentData, setCurrentData ] = useState({});
    
      useEffect(() => {
        const todoData = JSON.parse(localStorage.getItem("todoData") || '[]');
        setTodoList(todoData);
      }, []);
    
      useEffect(() => {
        localStorage.setItem("todoData", JSON.stringify(todoList));
      }, [todoList]);
      
      const addItem = useCallback(
        (value) => {
          const dataItem = {
            id: new Date().getTime(),
            content: value,
            completed: false,
          };
          setTodoList((todoList) => [...todoList, dataItem]);
          setIsShowInput(false);
        },
        []
      );
    
      const removeItem = useCallback((id) => {
        setTodoList((todoList) => todoList.filter((item) => item.id !== id));
      }, []);
    
      const completeItem = useCallback((id) => {
        setTodoList((todoList) => todoList.map((item) => {
          if (item.id === id) {
            item.completed = !item.completed;
          }
          return item;
        }));
      }, [])
    
      const openCheckModal = useCallback(
        (id) => {
          setCurrentData(() => todoList.filter((item) => item.id === id)[0]);
          setIsShowCheckModal(true);
        },
        [todoList]
      );
    
      const openEditModal = useCallback(
        (id) => {
          setCurrentData(() => todoList.filter((item) => item.id === id)[0]);
          setIsShowEditModal(true);
        },
        [todoList]
      );
    
      const submitEdit = useCallback(
        (newData, id) => {
          setTodoList((todoList) =>
            todoList.map((item) => {
              if (item.id === id) {
                item = newData;
              }
              return item;
            })
          );
          setIsShowEditModal(false);
        },
        []
      );
    
      return (
        <div className="App">
          <CheckModal
            isShowCheckModal={isShowCheckModal}
            data={currentData}
            closeModal={() => setIsShowCheckModal(false)}
          />
          <EditModal
            isShowEditModal={isShowEditModal}
            data={currentData}
            submitEdit={submitEdit}
          />
          <MyHeader openInput={() => setIsShowInput(!isShowInput)} />
          <AddInput isShow={isShowInput} addItem={(value) => addItem(value)} />
          {!todoList || todoList.length === 0 ? (
            <NoDataTip />
          ) : (
            <ul className="todo-list">
              {todoList.map((item, index) => {
                return (
                  <TodoItem
                    dataItem={item}
                    key={index}
                    removeItem={removeItem}
                    openCheckModal={openCheckModal}
                    completeItem={completeItem}
                    openEditModal={openEditModal}
                  />
                );
              })}
            </ul>
          )}
        </div>
      );
    }
    
    export default App;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123

    TodoItem.js

    import React from 'react';
    
    import './index.scss';
    
    function TodoItem (props) {
      const {
        removeItem,
        completeItem,
        openCheckModal,
        openEditModal,
        dataItem,
      } = props;
    
      return (
        <li className="todo-item">
          <div className="check-box">
            <input
              type="checkbox"
              checked={dataItem.completed}
              onChange={() => completeItem(dataItem.id)}
            />
          </div>
          <span
            className="content"
            style={{ textDecoration: dataItem.completed ? "line-through" : "none" }}
          >
            {dataItem.content}
          </span>
          <div className="btn-group">
            <button
              className="btn btn-primary"
              onClick={() => openCheckModal(dataItem.id)}
            >
              查看
            </button>
            <button
              className="btn btn-warning"
              onClick={() => openEditModal(dataItem.id)}
            >
              编辑
            </button>
            <button
              className="btn btn-danger"
              onClick={() => removeItem(dataItem.id)}
            >
              删除
            </button>
          </div>
        </li>
      );
    }
    
    export default TodoItem;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    日期格式化

    utils.js

    function _addZero(value) {
      return value < 10 ? "0" + value : value;
    }
    
    function formatDateTime (timeStamp) {
      const date = new Date(timeStamp);
    
      const y = date.getFullYear(),
            m = _addZero(date.getMonth() + 1),
            d = _addZero(date.getDate()),
            h = _addZero(date.getHours()),
            i = _addZero(date.getMinutes()),
            s = _addZero(date.getSeconds());
      
      return `${y}${m}${d}${h}:${i}:${s}`;
    }
    
    export {
      formatDateTime
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    Redux版本

    在这里插入图片描述

    1. 数据存在Store
    2. 想要更新数据使用Action,Action里面的type为数据类型,data为操作数据时候的所需的附加参数
    3. 通过Action实现状态数据的更新,需要使用reducer,reducer需要两个参数,一个是数据的前状态,第二个是Action,这就实现了数据的更新
    4. 数据更新显示在识图

    TodoHeader + TodoMain + TodoItem + TodoFooter

    常(constant) 爱(action) 瑞(redux) 组(component)
    在这里插入图片描述

    跑通Redux

    App.js

    import React from 'react'
    import TodoHeader from './components/TodoHeader'
    import TodoMain from './components/TodoMain'
    import TodoFooter from './components/TodoFooter'
    
    export default function App() {
      return (
        <section className='todoapp'>
          <TodoHeader />
          <TodoMain />
          <TodoFooter />
        </section>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    src/index.js

    import React from 'react'
    import ReactDOM from 'react-dom'
    import { Provider } from 'react-redux'
    import './styles/base.css'
    import './styles/index.css'
    import App from './App'
    import store from './store'
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.querySelector('#root')
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    TodoHeader.js

    import React from 'react'
    
    export default function TodoHeader() {
      return (
        <header className='header'>
          <h1>todos</h1>
          <input
            className='new-todo'
            placeholder='What needs to be done?'
            autoFocus
          />
        </header>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    TodoMain.js

    import React from 'react'
    
    export default function TodoMain() {
      return (
        <section className='main'>
          <input id='toggle-all' className='toggle-all' type='checkbox' />
          <label htmlFor='toggle-all'>Mark all as complete</label>
          <ul className='todo-list'>
            <li className='completed'>
              <div className='view'>
                <input
                  className='toggle'
                  type='checkbox'
                  checked
                  onChange={() => {}}
                />
                <label>Taste JavaScript</label>
                <button className='destroy'></button>
              </div>
              <input
                className='edit'
                value='Create a TodoMVC template'
                onChange={() => {}}
              />
            </li>
            <li>
              <div className='view'>
                <input className='toggle' type='checkbox' />
                <label>Buy a unicorn</label>
                <button className='destroy'></button>
              </div>
              <input className='edit' value='Rule the web' onChange={() => {}} />
            </li>
          </ul>
        </section>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    TodoFooter

    import React from 'react'
    
    export default function TodoFooter() {
      return (
        <footer className='footer'>
          <span className='todo-count'>
            <strong>0</strong> item left
          </span>
          <ul className='filters'>
            <li>
              <a className='selected' href='#/'>
                All
              </a>
            </li>
            <li>
              <a href='#/active'>Active</a>
            </li>
            <li>
              <a href='#/completed'>Completed</a>
            </li>
          </ul>
          <button className='clear-completed'>Clear completed</button>
        </footer>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    列表渲染

    src / store / reducers / todo.js

    const initState = [
      {
        id: 1,
        name: '吃饭',
        done: true,
      },
      {
        id: 2,
        name: '睡觉',
        done: false,
      },
    ]
    export default function todo(state = initState, action) {
      return state
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    src/store/reducers/index.js

    // 组合 reducers
    import { combineReducers } from 'redux'
    import todo from './todo'
    export default combineReducers({ todo })
    
    • 1
    • 2
    • 3
    • 4

    src/store/index.js

    import { createStore } from 'redux'
    import { composeWithDevTools } from 'redux-devtools-extension'
    import rootReducer from './reducers'
    export default createStore(rootReducer, composeWithDevTools())
    
    • 1
    • 2
    • 3
    • 4

    TodoMain.js

    import { useSelector } from 'react-redux'
    import classNames from 'classnames'
    
    export default function TodoMain() {
      const lists = useSelector((state) => state.todo)
      return (
        <section className='main'>
          <input id='toggle-all' className='toggle-all' type='checkbox' />
          <label htmlFor='toggle-all'>Mark all as complete</label>
          <ul className='todo-list'>
            {lists.map((item) => (
              <li
                key={item.id}
                className={classNames({
                  completed: item.done,
                })}
              >
                <div className='view'>
                  <input
                    className='toggle'
                    type='checkbox'
                    checked={item.done}
                    onChange={() => {}}
                  />
                  <label>{item.name}</label>
                  <button className='destroy'></button>
                </div>
                <input
                  className='edit'
                  value='Create a TodoMVC template'
                  onChange={() => {}}
                />
              </li>
            ))}
          </ul>
        </section>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    删除

    src/store/constants/todo.js

    export const TODO_DEL = 'TODO_DEL'
    
    • 1

    src/store/constants/index.js

    export { TODO_DEL } from './todo'
    
    • 1

    src/store/actions/todo.js

    import { TODO_DEL } from '../constants'
    
    export const todoDel = (id) => ({
      type: TODO_DEL,
      id,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    src/store/actions/index.js

    export { todoDel } from './todo'
    
    • 1

    src/store/reducers/todo.js

    import { TODO_DEL } from '../constants'
    
    const initState = [
      {
        id: 1,
        name: '吃饭',
        done: true,
      },
      {
        id: 2,
        name: '睡觉',
        done: false,
      },
    ]
    export default function todo(state = initState, action) {
      switch (action.type) {
        case TODO_DEL:
          return state.filter((item) => item.id !== action.id)
        default:
          return state
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    src/store/reducers/index.js

    // 组合 reducers
    import { combineReducers } from 'redux'
    import todo from './todo'
    export default combineReducers({ todo })
    
    • 1
    • 2
    • 3
    • 4

    TodoMain

    import { useSelector, useDispatch } from 'react-redux'
    import classNames from 'classnames'
    import { todoDel } from '../../store/actions/todo'
    // 常量 => actionCreator => action(常量) => Reducer => 组件
    // 常爱瑞组:优秀!
    
    export default function TodoMain() {
      const dispatch = useDispatch()
      const lists = useSelector((state) => state.todo)
      const handleDel = (id) => dispatch(todoDel(id))
      return (
        <section className='main'>
          <input id='toggle-all' className='toggle-all' type='checkbox' />
          <label htmlFor='toggle-all'>Mark all as complete</label>
          <ul className='todo-list'>
            {lists.map((item) => (
              <li
                key={item.id}
                className={classNames({
                  completed: item.done,
                })}
              >
                <div className='view'>
                  <input
                    className='toggle'
                    type='checkbox'
                    checked={item.done}
                    onChange={() => {}}
                  />
                  <label>{item.name}</label>
                  <button
                    className='destroy'
                    onClick={() => handleDel(item.id)}
                  ></button>
                </div>
                <input
                  className='edit'
                  value='Create a TodoMVC template'
                  onChange={() => {}}
                />
              </li>
            ))}
          </ul>
        </section>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    状态切换

    src/store/constants/todo.js

    export const TODO_DEL = 'TODO_DEL'
    export const TODO_CHANGE_DONE = 'TODO_CHANGE_DONE'
    
    • 1
    • 2

    src/store/constants/index.js

    export { TODO_DEL, TODO_CHANGE_DONE } from './todo'
    
    • 1

    src/store/actions/todo.js

    import { TODO_CHANGE_DONE, TODO_DEL } from '../constants'
    
    export const todoDel = (id) => ({
      type: TODO_DEL,
      id,
    })
    
    export const todoChangeDone = (id) => ({
      type: TODO_CHANGE_DONE,
      id,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    src/store/actions/index.js

    export { todoDel, todoChangeDone } from './todo'
    
    • 1

    src/store/reducers/todo.js

    import { TODO_CHANGE_DONE, TODO_DEL } from '../constants'
    
    const initState = [
      {
        id: 1,
        name: '吃饭',
        done: true,
      },
      {
        id: 2,
        name: '睡觉',
        done: false,
      },
    ]
    export default function todo(state = initState, action) {
      switch (action.type) {
        case TODO_DEL:
          return state.filter((item) => item.id !== action.id)
        case TODO_CHANGE_DONE:
          return state.map((item) =>
            item.id === action.id
              ? {
                  ...item,
                  done: !item.done,
                }
              : item
          )
        default:
          return state
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    TodoMain

    import { useSelector, useDispatch } from 'react-redux'
    import classNames from 'classnames'
    import { todoChangeDone, todoDel } from '../../store/actions/todo'
    // 常量 => actionCreator => action(常量) => Reducer => 组件
    // 常爱瑞组:优秀!
    
    export default function TodoMain() {
      const dispatch = useDispatch()
      const lists = useSelector((state) => state.todo)
      const handleDel = (id) => dispatch(todoDel(id))
      const handleChange = (id) => dispatch(todoChangeDone(id))
      return (
        <section className='main'>
          <input id='toggle-all' className='toggle-all' type='checkbox' />
          <label htmlFor='toggle-all'>Mark all as complete</label>
          <ul className='todo-list'>
            {lists.map((item) => (
              <li
                key={item.id}
                className={classNames({
                  completed: item.done,
                })}
              >
                <div className='view'>
                  <input
                    className='toggle'
                    type='checkbox'
                    checked={item.done}
                    onChange={() => handleChange(item.id)}
                  />
                  <label>{item.name}</label>
                  <button
                    className='destroy'
                    onClick={() => handleDel(item.id)}
                  ></button>
                </div>
                <input
                  className='edit'
                  value='Create a TodoMVC template'
                  onChange={() => {}}
                />
              </li>
            ))}
          </ul>
        </section>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    添加

    src/store/constants/todo.js

    export const TODO_DEL = 'TODO_DEL'
    export const TODO_CHANGE_DONE = 'TODO_CHANGE_DONE'
    export const TODO_ADD = 'TODO_ADD'
    
    • 1
    • 2
    • 3

    src/store/constants/index.js

    export { TODO_DEL, TODO_CHANGE_DONE, TODO_ADD } from './todo'
    
    • 1

    src/store/actions/todo.js

    import { TODO_ADD, TODO_CHANGE_DONE, TODO_DEL } from '../constants'
    
    export const todoDel = (id) => ({
      type: TODO_DEL,
      id,
    })
    
    export const todoChangeDone = (id) => ({
      type: TODO_CHANGE_DONE,
      id,
    })
    
    export const todoAdd = (name) => ({
      type: TODO_ADD,
      id: Date.now(),
      name,
      done: false,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    src/store/actions/index.js

    export { todoDel, todoChangeDone } from './todo'
    
    • 1

    src/store/reducers/todo.js

    import { TODO_ADD, TODO_CHANGE_DONE, TODO_DEL } from '../constants'
    
    const initState = [
      {
        id: 1,
        name: '吃饭',
        done: true,
      },
      {
        id: 2,
        name: '睡觉',
        done: false,
      },
    ]
    export default function todo(state = initState, action) {
      switch (action.type) {
        case TODO_DEL:
          return state.filter((item) => item.id !== action.id)
        case TODO_CHANGE_DONE:
          return state.map((item) =>
            item.id === action.id
              ? {
                  ...item,
                  done: !item.done,
                }
              : item
          )
        case TODO_ADD:
          const { type, ...rest } = action
          return [rest, ...state]
        default:
          return state
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    TodoHeader

    import { useState } from 'react'
    import { useDispatch } from 'react-redux'
    import { todoAdd } from '../../store/actions/todo'
    
    export default function TodoHeader() {
      const [name, setName] = useState('')
      const dispatch = useDispatch()
      const handleChange = (e) => setName(e.target.value)
      const handleKeyUp = (e) => {
        if (e.key === 'Enter') {
          if (name.trim().length === 0) return
          dispatch(todoAdd(name))
          setName('')
        }
      }
      return (
        <header className='header'>
          <h1>todos</h1>
          <input
            className='new-todo'
            placeholder='What needs to be done?'
            autoFocus
            value={name}
            onChange={handleChange}
            onKeyUp={handleKeyUp}
          />
        </header>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    TodoMain

    import { useSelector, useDispatch } from 'react-redux'
    import classNames from 'classnames'
    import { todoChangeDone, todoDel } from '../../store/actions/todo'
    // 常量 => actionCreator => action(常量) => Reducer => 组件
    // 常爱瑞组:优秀!
    
    export default function TodoMain() {
      const dispatch = useDispatch()
      // @ts-ignore
      const lists = useSelector((state) => state.todo)
      const handleDel = (id) => dispatch(todoDel(id))
      const handleChange = (id) => dispatch(todoChangeDone(id))
      return (
        <section className='main'>
          <input id='toggle-all' className='toggle-all' type='checkbox' />
          <label htmlFor='toggle-all'>Mark all as complete</label>
          <ul className='todo-list'>
            {lists.map((item) => (
              <li
                key={item.id}
                className={classNames({
                  completed: item.done,
                })}
              >
                <div className='view'>
                  <input
                    className='toggle'
                    type='checkbox'
                    checked={item.done}
                    onChange={() => handleChange(item.id)}
                  />
                  <label>{item.name}</label>
                  <button
                    className='destroy'
                    onClick={() => handleDel(item.id)}
                  ></button>
                </div>
                <input
                  className='edit'
                  value='Create a TodoMVC template'
                  onChange={() => {}}
                />
              </li>
            ))}
          </ul>
        </section>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    全选和取消

    src/store/constants/todo.js

    export const TODO_DEL = 'TODO_DEL'
    export const TODO_CHANGE_DONE = 'TODO_CHANGE_DONE'
    export const TODO_ADD = 'TODO_ADD'
    export const TODO_CHECK_ALL = 'TODO_CHECK_ALL'
    
    • 1
    • 2
    • 3
    • 4

    src/store/constants/index.js

    export { TODO_DEL, TODO_CHANGE_DONE, TODO_ADD, TODO_CHECK_ALL } from './todo'
    
    • 1

    src/store/actions/todo.js

    import {
      TODO_ADD,
      TODO_CHANGE_DONE,
      TODO_CHECK_ALL,
      TODO_DEL,
    } from '../constants'
    
    export const todoDel = (id) => ({
      type: TODO_DEL,
      id,
    })
    
    export const todoChangeDone = (id) => ({
      type: TODO_CHANGE_DONE,
      id,
    })
    
    export const todoAdd = (name) => ({
      type: TODO_ADD,
      id: Date.now(),
      name,
      done: false,
    })
    
    export const todoCheckAll = (done) => ({
      type: TODO_CHECK_ALL,
      done,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    src/store/reducers/todo.js

    import {
      TODO_ADD,
      TODO_CHANGE_DONE,
      TODO_CHECK_ALL,
      TODO_DEL,
    } from '../constants'
    
    const initState = [
      {
        id: 1,
        name: '吃饭',
        done: true,
      },
      {
        id: 2,
        name: '睡觉',
        done: false,
      },
    ]
    export default function todo(state = initState, action) {
      switch (action.type) {
        case TODO_DEL:
          return state.filter((item) => item.id !== action.id)
        case TODO_CHANGE_DONE:
          return state.map((item) =>
            item.id === action.id
              ? {
                  ...item,
                  done: !item.done,
                }
              : item
          )
        case TODO_ADD:
          const { type, ...rest } = action
          return [rest, ...state]
        case TODO_CHECK_ALL:
          return state.map((item) => ({ ...item, done: action.done }))
        default:
          return state
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    TodoMain

    import { useSelector, useDispatch } from 'react-redux'
    import classNames from 'classnames'
    import { todoChangeDone, todoCheckAll, todoDel } from '../../store/actions/todo'
    // 常量 => actionCreator => action(常量) => Reducer => 组件
    // 常爱瑞组:优秀!
    
    export default function TodoMain() {
      const dispatch = useDispatch()
      // @ts-ignore
      const lists = useSelector((state) => state.todo)
      const handleDel = (id) => dispatch(todoDel(id))
      const handleChange = (id) => dispatch(todoChangeDone(id))
      // !传递什么:当前状态的基础上进行取反
      const handleChangeAll = () => dispatch(todoCheckAll(!nowStatus))
      const nowStatus = lists.every((item) => item.done)
      return (
        <section className='main'>
          <input
            id='toggle-all'
            className='toggle-all'
            type='checkbox'
            checked={nowStatus}
            onChange={handleChangeAll}
          />
          <label htmlFor='toggle-all'>Mark all as complete</label>
          <ul className='todo-list'>
            {lists.map((item) => (
              <li
                key={item.id}
                className={classNames({
                  completed: item.done,
                })}
              >
                <div className='view'>
                  <input
                    className='toggle'
                    type='checkbox'
                    checked={item.done}
                    onChange={() => handleChange(item.id)}
                  />
                  <label>{item.name}</label>
                  <button
                    className='destroy'
                    onClick={() => handleDel(item.id)}
                  ></button>
                </div>
                <input
                  className='edit'
                  value='Create a TodoMVC template'
                  onChange={() => {}}
                />
              </li>
            ))}
          </ul>
        </section>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    双击展示编辑框

    TodoMain

    import { useState } from 'react'
    import { useSelector, useDispatch } from 'react-redux'
    import classNames from 'classnames'
    import { todoChangeDone, todoCheckAll, todoDel } from '../../store/actions/todo'
    
    export default function TodoMain() {
      const dispatch = useDispatch()
      // @ts-ignore
      const lists = useSelector((state) => state.todo)
      const [currentId, setCurrentId] = useState('')
      const handleDel = (id) => dispatch(todoDel(id))
      const handleChange = (id) => dispatch(todoChangeDone(id))
      // !传递什么:当前状态的基础上进行取反
      const handleChangeAll = () => dispatch(todoCheckAll(!nowStatus))
      const nowStatus = lists.every((item) => item.done)
      // 1. 准备一个变量(状态)
      // 2. 用变量和循环时候的 id 进行比较,一样的就使用 editing class
      // 3. 双击的时候改变这个变量,改成当前双击时候的 id
      const handleDblClick = (id) => setCurrentId(id)
      return (
        <section className='main'>
          <input
            id='toggle-all'
            className='toggle-all'
            type='checkbox'
            checked={nowStatus}
            onChange={handleChangeAll}
          />
          <label htmlFor='toggle-all'>Mark all as complete</label>
          <ul className='todo-list'>
            {lists.map((item) => (
              <li
                key={item.id}
                className={classNames({
                  completed: item.done,
                  editing: currentId === item.id,
                })}
              >
                <div className='view'>
                  <input
                    className='toggle'
                    type='checkbox'
                    checked={item.done}
                    onChange={() => handleChange(item.id)}
                  />
                  <label onDoubleClick={() => handleDblClick(item.id)}>
                    {item.name}
                  </label>
                  <button
                    className='destroy'
                    onClick={() => handleDel(item.id)}
                  ></button>
                </div>
                <input
                  className='edit'
                  value='Create a TodoMVC template'
                  onChange={() => {}}
                />
              </li>
            ))}
          </ul>
        </section>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    双击聚焦输入框

    ToDoItem

    import classNames from 'classnames'
    import { useState, useRef, useEffect } from 'react'
    import { useDispatch } from 'react-redux'
    import { todoChangeDone, todoDel } from '../../store/actions/todo'
    export default function TodoItem({ item }) {
      const inputRef = useRef(null)
      const dispatch = useDispatch()
      const [currentId, setCurrentId] = useState('')
      const handleDel = (id) => dispatch(todoDel(id))
      const handleChange = (id) => dispatch(todoChangeDone(id))
      const handleDblClick = (id) => setCurrentId(id)
      const handleBlur = () => setCurrentId('')
      useEffect(() => {
        inputRef.current.focus()
      }, [currentId])
      return (
        <li
          className={classNames({
            completed: item.done,
            editing: currentId === item.id,
          })}
        >
          <div className='view'>
            <input
              className='toggle'
              type='checkbox'
              checked={item.done}
              onChange={() => handleChange(item.id)}
            />
            <label onDoubleClick={() => handleDblClick(item.id)}>{item.name}</label>
            <button className='destroy' onClick={() => handleDel(item.id)}></button>
          </div>
          <input
            className='edit'
            value='Create a TodoMVC template'
            onChange={() => {}}
            ref={inputRef}
            onBlur={handleBlur}
          />
        </li>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    TodoMain

    import { useSelector, useDispatch } from 'react-redux'
    import { todoCheckAll } from '../../store/actions/todo'
    import TodoItem from '../TodoItem'
    
    export default function TodoMain() {
      const dispatch = useDispatch()
      // @ts-ignore
      const lists = useSelector((state) => state.todo)
      const handleChangeAll = () => dispatch(todoCheckAll(!nowStatus))
      const nowStatus = lists.every((item) => item.done)
      return (
        <section className='main'>
          <input
            id='toggle-all'
            className='toggle-all'
            type='checkbox'
            checked={nowStatus}
            onChange={handleChangeAll}
          />
          <label htmlFor='toggle-all'>Mark all as complete</label>
          <ul className='todo-list'>
            {lists.map((item) => (
              <TodoItem item={item} key={item.id} />
            ))}
          </ul>
        </section>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    双击回显

    TodoItem

    import classNames from 'classnames'
    import { useState, useRef, useEffect } from 'react'
    import { useDispatch } from 'react-redux'
    import { todoChangeDone, todoDel } from '../../store/actions/todo'
    export default function TodoItem({ item }) {
      const inputRef = useRef(null)
      const dispatch = useDispatch()
      const [currentId, setCurrentId] = useState('')
      const [currentName, setCurrentName] = useState('')
      const handleDel = (id) => dispatch(todoDel(id))
      const handleChange = (id) => dispatch(todoChangeDone(id))
      const handleDblClick = (id) => {
        setCurrentId(id)
        setCurrentName(item.name)
      }
      const handleBlur = () => setCurrentId('')
      // 先把输入的数据挤下来,敲回车的时候再更新到 Redux
      const handleEditChange = (e) => {
        setCurrentName(e.target.value)
      }
      useEffect(() => {
        inputRef.current.focus()
      }, [currentId])
      return (
        <li
          className={classNames({
            completed: item.done,
            editing: currentId === item.id,
          })}
        >
          <div className='view'>
            <input
              className='toggle'
              type='checkbox'
              checked={item.done}
              onChange={() => handleChange(item.id)}
            />
            <label onDoubleClick={() => handleDblClick(item.id)}>{item.name}</label>
            <button className='destroy' onClick={() => handleDel(item.id)}></button>
          </div>
          <input
            className='edit'
            value={currentName}
            onChange={handleEditChange}
            ref={inputRef}
            onBlur={handleBlur}
          />
        </li>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    修改Todo

    src/store/constants/todo.js

    export const TODO_DEL = 'TODO_DEL'
    export const TODO_CHANGE_DONE = 'TODO_CHANGE_DONE'
    export const TODO_ADD = 'TODO_ADD'
    export const TODO_CHECK_ALL = 'TODO_CHECK_ALL'
    export const TODO_MODIFY_NAME = 'TODO_MODIFY_NAME'
    
    • 1
    • 2
    • 3
    • 4
    • 5

    src/store/constants/index.js

    export {
      TODO_DEL,
      TODO_CHANGE_DONE,
      TODO_ADD,
      TODO_CHECK_ALL,
      TODO_MODIFY_NAME,
    } from './todo'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    src/store/actions/todo.js

    import {
      TODO_ADD,
      TODO_CHANGE_DONE,
      TODO_CHECK_ALL,
      TODO_DEL,
      TODO_MODIFY_NAME,
    } from '../constants'
    
    export const todoDel = (id) => ({
      type: TODO_DEL,
      id,
    })
    
    export const todoChangeDone = (id) => ({
      type: TODO_CHANGE_DONE,
      id,
    })
    
    export const todoAdd = (name) => ({
      type: TODO_ADD,
      id: Date.now(),
      name,
      done: false,
    })
    
    export const todoCheckAll = (done) => ({
      type: TODO_CHECK_ALL,
      done,
    })
    
    export const todoModifyName = (id, name) => ({
      type: TODO_MODIFY_NAME,
      id,
      name,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    src/store/actions/index.js

    export {
      todoDel,
      todoChangeDone,
      todoAdd,
      todoCheckAll,
      todoModifyName,
    } from './todo'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    src/store/reducers/todo.js

    import {
      TODO_ADD,
      TODO_CHANGE_DONE,
      TODO_CHECK_ALL,
      TODO_DEL,
      TODO_MODIFY_NAME,
    } from '../constants'
    
    const initState = [
      {
        id: 1,
        name: '吃饭',
        done: true,
      },
      {
        id: 2,
        name: '睡觉',
        done: false,
      },
    ]
    export default function todo(state = initState, action) {
      switch (action.type) {
        case TODO_DEL:
          return state.filter((item) => item.id !== action.id)
        case TODO_CHANGE_DONE:
          return state.map((item) =>
            item.id === action.id
              ? {
                  ...item,
                  done: !item.done,
                }
              : item
          )
        case TODO_ADD:
          const { type, ...rest } = action
          return [rest, ...state]
        case TODO_CHECK_ALL:
          return state.map((item) => ({ ...item, done: action.done }))
        case TODO_MODIFY_NAME:
          return state.map((item) =>
            item.id === action.id
              ? {
                  ...item,
                  name: action.name,
                }
              : item
          )
        default:
          return state
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    TodoItem

    import classNames from 'classnames'
    import { useState, useRef, useEffect } from 'react'
    import { useDispatch } from 'react-redux'
    import { todoChangeDone, todoDel, todoModifyName } from '../../store/actions'
    export default function TodoItem({ item }) {
      const inputRef = useRef(null)
      const dispatch = useDispatch()
      const [currentId, setCurrentId] = useState('')
      const [currentName, setCurrentName] = useState('')
      const handleDel = (id) => dispatch(todoDel(id))
      const handleChange = (id) => dispatch(todoChangeDone(id))
      const handleDblClick = (id) => {
        setCurrentId(id)
        setCurrentName(item.name)
      }
      const handleBlur = () => setCurrentId('')
      // 先把输入的数据挤下来,敲回车的时候再更新到 Redux
      const handleEditChange = (e) => {
        setCurrentName(e.target.value)
      }
      const handleKeyUp = (e) => {
        if (e.key === 'Enter') {
          dispatch(todoModifyName(item.id, currentName))
          setCurrentId('')
          setCurrentName('')
        }
      }
      useEffect(() => {
        inputRef.current.focus()
      }, [currentId])
      return (
        <li
          className={classNames({
            completed: item.done,
            editing: currentId === item.id,
          })}
        >
          <div className='view'>
            <input
              className='toggle'
              type='checkbox'
              checked={item.done}
              onChange={() => handleChange(item.id)}
            />
            <label onDoubleClick={() => handleDblClick(item.id)}>{item.name}</label>
            <button className='destroy' onClick={() => handleDel(item.id)}></button>
          </div>
          <input
            className='edit'
            value={currentName}
            onChange={handleEditChange}
            ref={inputRef}
            onBlur={handleBlur}
            onKeyUp={handleKeyUp}
          />
        </li>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    清空已完成

    src/store/constants/todo.js

    export const TODO_DEL = 'TODO_DEL'
    export const TODO_CHANGE_DONE = 'TODO_CHANGE_DONE'
    export const TODO_ADD = 'TODO_ADD'
    export const TODO_CHECK_ALL = 'TODO_CHECK_ALL'
    export const TODO_MODIFY_NAME = 'TODO_MODIFY_NAME'
    export const TODO_CLEAR_DONED = 'TODO_CLEAR_DONED'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    src/store/constants/index.js

    export {
      TODO_DEL,
      TODO_CHANGE_DONE,
      TODO_ADD,
      TODO_CHECK_ALL,
      TODO_MODIFY_NAME,
      TODO_CLEAR_DONED,
    } from './todo'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    src/store/actions/todo.js

    import {
      TODO_ADD,
      TODO_CHANGE_DONE,
      TODO_CHECK_ALL,
      TODO_CLEAR_DONED,
      TODO_DEL,
      TODO_MODIFY_NAME,
    } from '../constants'
    
    export const todoDel = (id) => ({
      type: TODO_DEL,
      id,
    })
    
    export const todoChangeDone = (id) => ({
      type: TODO_CHANGE_DONE,
      id,
    })
    
    export const todoAdd = (name) => ({
      type: TODO_ADD,
      id: Date.now(),
      name,
      done: false,
    })
    
    export const todoCheckAll = (done) => ({
      type: TODO_CHECK_ALL,
      done,
    })
    
    export const todoModifyName = (id, name) => ({
      type: TODO_MODIFY_NAME,
      id,
      name,
    })
    
    export const todoClearDoned = () => ({
      type: TODO_CLEAR_DONED,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    src/store/reducers/todo.js

    import {
      TODO_ADD,
      TODO_CHANGE_DONE,
      TODO_CHECK_ALL,
      TODO_CLEAR_DONED,
      TODO_DEL,
      TODO_MODIFY_NAME,
    } from '../constants'
    
    const initState = [
      {
        id: 1,
        name: '吃饭',
        done: true,
      },
      {
        id: 2,
        name: '睡觉',
        done: false,
      },
    ]
    export default function todo(state = initState, action) {
      switch (action.type) {
        case TODO_DEL:
          return state.filter((item) => item.id !== action.id)
        case TODO_CHANGE_DONE:
          return state.map((item) =>
            item.id === action.id
              ? {
                  ...item,
                  done: !item.done,
                }
              : item
          )
        case TODO_ADD:
          const { type, ...rest } = action
          return [rest, ...state]
        case TODO_CHECK_ALL:
          return state.map((item) => ({ ...item, done: action.done }))
        case TODO_MODIFY_NAME:
          return state.map((item) =>
            item.id === action.id
              ? {
                  ...item,
                  name: action.name,
                }
              : item
          )
        case TODO_CLEAR_DONED:
          return state.filter((item) => !item.done)
        default:
          return state
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    TodoFooter

    import { useDispatch } from 'react-redux'
    import { todoClearDoned } from '../../store/actions/todo'
    
    export default function TodoFooter() {
      const dispatch = useDispatch()
      const handleClearDoned = () => dispatch(todoClearDoned())
      return (
        <footer className='footer'>
          <span className='todo-count'>
            <strong>0</strong> item left
          </span>
          <ul className='filters'>
            <li>
              <a className='selected' href='#/'>
                All
              </a>
            </li>
            <li>
              <a href='#/active'>Active</a>
            </li>
            <li>
              <a href='#/completed'>Completed</a>
            </li>
          </ul>
          <button className='clear-completed' onClick={handleClearDoned}>
            Clear completed
          </button>
        </footer>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    剩余数量的统计

    TodoFooter

    import { useDispatch, useSelector } from 'react-redux'
    import { todoClearDoned } from '../../store/actions/todo'
    
    export default function TodoFooter() {
      const dispatch = useDispatch()
      // @ts-ignore
      const lists = useSelector((state) => state.todo)
      const leftCount = lists.filter((item) => !item.done).length
      const handleClearDoned = () => dispatch(todoClearDoned())
      return (
        <footer className='footer'>
          <span className='todo-count'>
            <strong>{leftCount}</strong> item left
          </span>
          <ul className='filters'>
            <li>
              <a className='selected' href='#/'>
                All
              </a>
            </li>
            <li>
              <a href='#/active'>Active</a>
            </li>
            <li>
              <a href='#/completed'>Completed</a>
            </li>
          </ul>
          <button className='clear-completed' onClick={handleClearDoned}>
            Clear completed
          </button>
        </footer>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    点击高亮

    src/store/constants/filter.js

    export const FILTER_ACTIVE = 'FILTER_ACTIVE'
    
    • 1

    src/store/constants/index.js

    export {
      TODO_DEL,
      TODO_CHANGE_DONE,
      TODO_ADD,
      TODO_CHECK_ALL,
      TODO_MODIFY_NAME,
      TODO_CLEAR_DONED,
    } from './todo'
    
    export { FILTER_ACTIVE } from './filter'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    src/store/actions/todo.js

    import { FILTER_ACTIVE } from '../constants'
    
    export const filterActive = (active) => ({
      type: FILTER_ACTIVE,
      active,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    src/store/reducers/filter.js

    import { FILTER_ACTIVE } from '../constants'
    
    const initState = {
      arr: ['all', 'active', 'completed'],
      active: 'all',
    }
    export default function filter(state = initState, action) {
      switch (action.type) {
        case FILTER_ACTIVE:
          return {
            ...state,
            active: action.active,
          }
        default:
          return state
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    src/store/reducers/index.js

    // 组合 reducers
    import { combineReducers } from 'redux'
    import todo from './todo'
    import filter from './filter'
    export default combineReducers({ todo, filter })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    TodoFooter

    import classNames from 'classnames'
    import { useDispatch, useSelector } from 'react-redux'
    import { filterActive } from '../../store/actions/filter'
    
    import { todoClearDoned } from '../../store/actions/todo'
    
    export default function TodoFooter() {
      const dispatch = useDispatch()
      // @ts-ignore
      const lists = useSelector((state) => state.todo)
      // @ts-ignore
      const { arr, active } = useSelector((state) => state.filter)
      const leftCount = lists.filter((item) => !item.done).length
      const handleClearDoned = () => dispatch(todoClearDoned())
      const handleActive = (item) => dispatch(filterActive(item))
      return (
        <footer className='footer'>
          <span className='todo-count'>
            <strong>{leftCount}</strong> item left
          </span>
          <ul className='filters'>
            {arr.map((item) => (
              <li key={item} onClick={() => handleActive(item)}>
                <a
                  className={classNames({
                    selected: active === item,
                  })}
                  href='#/'
                >
                  {item}
                </a>
              </li>
            ))}
          </ul>
          <button className='clear-completed' onClick={handleClearDoned}>
            Clear completed
          </button>
        </footer>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    切换功能

    src/components/TodoMain/index.js

    import { useSelector, useDispatch } from 'react-redux'
    import { todoCheckAll } from '../../store/actions/todo'
    import TodoItem from '../TodoItem'
    
    export default function TodoMain() {
      const dispatch = useDispatch()
    
      const lists = useSelector((state) => {
        // @ts-ignore
        const ac = state.filter.active
        if (ac === 'active') {
          // @ts-ignore
          return state.todo.filter((item) => !item.done)
        }
        if (ac === 'completed') {
          // @ts-ignore
          return state.todo.filter((item) => item.done)
        }
        // @ts-ignore
        return state.todo
      })
      const handleChangeAll = () => dispatch(todoCheckAll(!nowStatus))
      const nowStatus = lists.every((item) => item.done)
      return (
        <section className='main'>
          <input
            id='toggle-all'
            className='toggle-all'
            type='checkbox'
            checked={nowStatus}
            onChange={handleChangeAll}
          />
          <label htmlFor='toggle-all'>Mark all as complete</label>
          <ul className='todo-list'>
            {lists.map((item) => (
              <TodoItem item={item} key={item.id} />
            ))}
          </ul>
        </section>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    完成效果

    TodoFooter

    import { useSelector, useDispatch } from 'react-redux'
    import { todoCheckAll } from '../../store/actions/todo'
    import TodoItem from '../TodoItem'
    
    export default function TodoMain() {
      const dispatch = useDispatch()
    
      const lists = useSelector((state) => {
        // @ts-ignore
        const ac = state.filter.active
        if (ac === 'active') {
          // @ts-ignore
          return state.todo.filter((item) => !item.done)
        }
        if (ac === 'completed') {
          // @ts-ignore
          return state.todo.filter((item) => item.done)
        }
        // @ts-ignore
        return state.todo
      })
      const handleChangeAll = () => dispatch(todoCheckAll(!nowStatus))
      const nowStatus = lists.every((item) => item.done)
      return (
        <section className='main'>
          <input
            id='toggle-all'
            className='toggle-all'
            type='checkbox'
            checked={nowStatus}
            onChange={handleChangeAll}
          />
          <label htmlFor='toggle-all'>Mark all as complete</label>
          <ul className='todo-list'>
            {lists.map((item) => (
              <TodoItem item={item} key={item.id} />
            ))}
          </ul>
        </section>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    TodoItem

    import classNames from 'classnames'
    import { useState, useRef, useEffect } from 'react'
    import { useDispatch } from 'react-redux'
    import { todoChangeDone, todoDel, todoModifyName } from '../../store/actions'
    export default function TodoItem({ item }) {
      const inputRef = useRef(null)
      const dispatch = useDispatch()
      const [currentId, setCurrentId] = useState('')
      const [currentName, setCurrentName] = useState('')
      const handleDel = (id) => dispatch(todoDel(id))
      const handleChange = (id) => dispatch(todoChangeDone(id))
      const handleDblClick = (id, name) => {
        setCurrentId(id)
        setCurrentName(name)
      }
      const handleBlur = () => setCurrentId('')
      // 先把输入的数据挤下来,敲回车的时候再更新到 Redux
      const handleEditChange = (e) => setCurrentName(e.target.value)
      const handleKeyUp = (e) => {
        if (e.key === 'Escape') return handleDblClick('', '')
        if (e.key === 'Enter') {
          dispatch(todoModifyName(item.id, currentName))
          handleDblClick('', '')
        }
      }
      useEffect(() => inputRef.current.focus(), [currentId])
      return (
        <li
          className={classNames({
            completed: item.done,
            editing: currentId === item.id,
          })}
        >
          <div className='view'>
            <input
              className='toggle'
              type='checkbox'
              checked={item.done}
              onChange={() => handleChange(item.id)}
            />
            <label onDoubleClick={() => handleDblClick(item.id, item.name)}>
              {item.name}
            </label>
            <button className='destroy' onClick={() => handleDel(item.id)}></button>
          </div>
          <input
            className='edit'
            value={currentName}
            onChange={handleEditChange}
            ref={inputRef}
            onBlur={handleBlur}
            onKeyUp={handleKeyUp}
          />
        </li>
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    Redux数据持久化

    src/index.js

    import React from 'react'
    import ReactDOM from 'react-dom'
    import { Provider } from 'react-redux'
    import { persistStore } from 'redux-persist'
    import { PersistGate } from 'redux-persist/integration/react'
    import './styles/base.css'
    import './styles/index.css'
    import App from './App'
    import store from './store'
    const persistor = persistStore(store)
    
    ReactDOM.render(
      <Provider store={store}>
        <PersistGate loading={null} persistor={persistor}>
          <App />
        </PersistGate>
      </Provider>,
      document.querySelector('#root')
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    src/store/index.js

    import { createStore } from 'redux'
    import { persistReducer } from 'redux-persist'
    import storage from 'redux-persist/lib/storage'
    import { composeWithDevTools } from 'redux-devtools-extension'
    import rootReducer from './reducers'
    const persistConfig = { key: '@TODO@', storage }
    const persistedReducer = persistReducer(persistConfig, rootReducer)
    export default createStore(persistedReducer, composeWithDevTools())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    异步处理

    src/store/actions/filter.js

    import { FILTER_ACTIVE } from '../constants'
    
    export const filterActiveAc = (active) => ({ type: FILTER_ACTIVE, active })
    
    export const filterActive = (active) => {
      // 一旦配置 redux-thunk 中间件,这里就支持返回返回的形式啦
      return (dispatch) => {
        setTimeout(() => {
          dispatch(filterActiveAc(active))
        }, 2000)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    src/store/index.js

    import thunk from 'redux-thunk'
    import { createStore, applyMiddleware } from 'redux'
    import { persistReducer } from 'redux-persist'
    import storage from 'redux-persist/lib/storage'
    import { composeWithDevTools } from 'redux-devtools-extension'
    import rootReducer from './reducers'
    const persistConfig = { key: '@TODO@', storage }
    const persistedReducer = persistReducer(persistConfig, rootReducer)
    export default createStore(
      persistedReducer,
      composeWithDevTools(applyMiddleware(thunk))
    )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    src/store/index.js

    import thunk from 'redux-thunk'
    import { createStore, applyMiddleware } from 'redux'
    import { persistReducer } from 'redux-persist'
    import storage from 'redux-persist/lib/storage'
    import { composeWithDevTools } from 'redux-devtools-extension'
    import rootReducer from './reducers'
    const persistConfig = { key: '@TODO@', storage }
    const persistedReducer = persistReducer(persistConfig, rootReducer)
    export default createStore(
      persistedReducer,
      composeWithDevTools(applyMiddleware(thunk))
    )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    Android12 适配 usb 触屏
    JavaEE-http/https/Tomcat(下)
    浏览器扩展V3开发系列之 chrome.commands 快捷键的用法和案例
    k8s — Cluster Architecture
    Docker的网络模式
    SuperMap iServer11i新功能----图例的发布和使用
    如何对需求变更进行精准的风险评估?
    DER编码
    巧用自定义函数,文本控件秒变高速缓存
    布隆过滤器
  • 原文地址:https://blog.csdn.net/qq_35218523/article/details/125505354