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;

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;

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;

输入增加待办事项

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>
      )}
    </>
  );
}

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;

遍历数据循环渲染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;

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;

数据存储到localStorage

useeffct用法
App.js

  useEffect(() => {
    const todoData = JSON.parse(localStorage.getItem("todoData") || '[]');
    setTodoList(todoData);
  }, []);

  useEffect(() => {
    localStorage.setItem("todoData", JSON.stringify(todoList));
  }, [todoList]);

模态框组件与插槽

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

基础容器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;

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;

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;

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;

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;

日期格式化

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
}

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>
  )
}

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')
)

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>
  )
}

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>
  )
}

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>
  )
}

列表渲染

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
}

src/store/reducers/index.js

// 组合 reducers
import { combineReducers } from 'redux'
import todo from './todo'
export default combineReducers({ todo })

src/store/index.js

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

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>
  )
}

删除

src/store/constants/todo.js

export const TODO_DEL = 'TODO_DEL'

src/store/constants/index.js

export { TODO_DEL } from './todo'

src/store/actions/todo.js

import { TODO_DEL } from '../constants'

export const todoDel = (id) => ({
  type: TODO_DEL,
  id,
})

src/store/actions/index.js

export { todoDel } from './todo'

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
  }
}

src/store/reducers/index.js

// 组合 reducers
import { combineReducers } from 'redux'
import todo from './todo'
export default combineReducers({ todo })

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>
  )
}

状态切换

src/store/constants/todo.js

export const TODO_DEL = 'TODO_DEL'
export const TODO_CHANGE_DONE = 'TODO_CHANGE_DONE'

src/store/constants/index.js

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

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,
})

src/store/actions/index.js

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

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
  }
}

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>
  )
}

添加

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'

src/store/constants/index.js

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

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,
})

src/store/actions/index.js

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

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
  }
}

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>
  )
}

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>
  )
}

全选和取消

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'

src/store/constants/index.js

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

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,
})

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
  }
}

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>
  )
}

双击展示编辑框

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>
  )
}

双击聚焦输入框

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>
  )
}

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>
  )
}

双击回显

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>
  )
}

修改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'

src/store/constants/index.js

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

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,
})

src/store/actions/index.js

export {
  todoDel,
  todoChangeDone,
  todoAdd,
  todoCheckAll,
  todoModifyName,
} from './todo'

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
  }
}

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>
  )
}

清空已完成

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'

src/store/constants/index.js

export {
  TODO_DEL,
  TODO_CHANGE_DONE,
  TODO_ADD,
  TODO_CHECK_ALL,
  TODO_MODIFY_NAME,
  TODO_CLEAR_DONED,
} from './todo'

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,
})

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
  }
}

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>
  )
}

剩余数量的统计

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>
  )
}

点击高亮

src/store/constants/filter.js

export const FILTER_ACTIVE = 'FILTER_ACTIVE'

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'

src/store/actions/todo.js

import { FILTER_ACTIVE } from '../constants'

export const filterActive = (active) => ({
  type: FILTER_ACTIVE,
  active,
})

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
  }
}

src/store/reducers/index.js

// 组合 reducers
import { combineReducers } from 'redux'
import todo from './todo'
import filter from './filter'
export default combineReducers({ todo, filter })

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>
  )
}

切换功能

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>
  )
}

完成效果

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>
  )
}

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>
  )
}

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')
)

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())

异步处理

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)
  }
}

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))
)

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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值