react-redux-toolkit

7/19/2020 react

# redux-toolkit


# 创建项目


npx react-create-app redux-toolkit-demo 
1

# 安装依赖


NPM
npm install @reduxjs/toolkit

Yarn
yarn add @reduxjs/toolkit



npm install redux
1
2
3
4
5
6
7
8
9

# 代码分层 目录结构


---store index.js ---modules counter.js ....

# 创建store


src\store\index.js

import { configureStore } from "@reduxjs/toolkit";
import counter from './modules/counter'
export  default configureStore({
    reducer: {
        counter,
        ...
    },
})
1
2
3
4
5
6
7
8

# 提供reducer模块


src\store\modules\counter.js

 
import { createSlice } from '@reduxjs/toolkit'

const counterSlice = createSlice({
    // 类似于命名空间,name 值会作为action type 的前缀
    name: 'counter',
    // 初始化状态
    initialState:{
        count: 0,
        list: []
    },
    // 1. 定义reducer更新状态函数
    // 2. 组件中dispatch使用的action 函数
    // 内置 immutable.js 插件  不可变值
    reducers: {
        add(state, action) {
            console.log(state, action)
            state.count ++ 
        },
        sub(state){
            state.count --
        },
        pus(state) {
            state.list.push(Math.floor(Math.random() * 100))
        },
        del(state, action){
            console.log(state, action)
            state.list.splice(action.plyload, 1)
        }
    }
})

console.log("counterSlice", counterSlice)
/**
 *  actions: {add: ƒ}
    caseReducers: {add: ƒ}
    getInitialState: ƒ ()
    name: "counter"
    reducer: ƒ (state, action)
 */

export const { add, sub, pus, del } = counterSlice.actions


// 定义异步action 
export const subAsync = (plyload) => {
    return async(dispatch, getState) => {
        setTimeout(() => {
            dispatch(sub())
        }, 3000)
    }
}


// 导出reducer, 创建store
export default counterSlice.reducer
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


import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import store from './store'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 使用dispatch actions



import './App.css';
import { useDispatch, useSelector } from 'react-redux'
import { add, subAsync, pus, del } from './store/modules/counter'
function App() {
  const dispatch = useDispatch()
  const { count, list } = useSelector(state => state.counter)
  const handleClickAdd = () => {
    dispatch(add())
  }

  const handleClickSub = () => {
    dispatch(subAsync())
  }

  const handleClickPus = () => {
    dispatch(pus())
  }

  const handleClickDel = (index) => {
    dispatch(del(index))
  }
  return (
    <div className="App">
      <h1>{count}</h1>
      <hr />
      <button onClick={handleClickAdd}>add</button>
      <hr />
      <button onClick={handleClickSub}>sub</button>
      <hr />
      <ul>
        {
          list.map((item,index) => <li onClick={() => handleClickDel(index)} key={index+item}>{item}---{index}</li>)
        }
      </ul>
      <button onClick={handleClickPus}>sub</button>
    </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

# 练习示例

安装

// 创建项目
yarn create vite react-redux --template react
// 安装redux配套工具
yarn add  @reduxjs/toolkit react-redux
// 启动项目
yarn dev
1
2
3
4
5
6

新建store目录结构 src

  • store
    • modules // 模块store
    • index.js // 组合模块的入口文件
  • App.js

src/store/modules/counterStore.js

// 创建子模块 counterStore.js

import { createSlice } from '@reduxjs/toolkit'


const counter = createSlice({
  // 模块名称独一无二
  name: 'counter',
  // 初始化数据
  initialState: {
    count: 0,
    listData: ['react'],
    asyncData: []
  },
  // 修改数据的同步方法
  reducers: {
    add (state) {
      state.count++
    },
    addList (state, action) {
      state.listData.push(action.payload)
    },
    getAsyncData (state, action) {
      state.asyncData = action.payload
    }
  }
})

// 导出修改数据的函数
export const { add, addList, getAsyncData } = counter.actions

// 导出reducer
const reducer = counter.reducer
export default reducer
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

src/store/index.js

import { configureStore } from '@reduxjs/toolkit'
import counterStore from './modules/counterStore'
// configureStore 语法组合子模块
export default configureStore({
  reducer: {
    // 注册子模块
    counterStore
  }
})
1
2
3
4
5
6
7
8
9

App入口提供 Store

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

// 导入 store
import store from './store'

// 导入store提供组件Provider
import { Provider } from 'react-redux'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

组件中使用store

import { useEffect, useRef } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { add, addList, getAsyncData } from './store/modules/counterStore'
import axios from 'axios'

function App () {
  // 使用数据
  const { count, listData, asyncData } = useSelector(state => state.counterStore)
  // 修改数据
  const dispatch = useDispatch()

  const handleClick = () => {
    // 1. 生成action 对象
    const action = add()
    // 2. 提交action进行数据更新
    dispatch(action)
  }

  const handleAddList = () => {
    // 1. 生成action
    const action = addList('vue')
    // 2. 提交action进行数据更新
    dispatch(action)
  }

  const renderRef = useRef(true)
  useEffect(() => {
    if (renderRef.current) {
      renderRef.current = false
      return
    }

    const getList = async () => {
      const url = 'http://geek.itheima.net/v1_0/channels'
      const res = await axios.get(url)
      console.log(res.data.data.channels)
      dispatch(getAsyncData(res.data.data.channels))
    }
    getList()

  }, [dispatch])

  return (
    <div className="App">
      <h1>{count}</h1>
      <button onClick={handleClick}>更新</button>
      <hr />
      <h1>{listData.join('-')}</h1>
      <button onClick={handleAddList}>更新列表</button>
      <hr />
      <ul>
        {
          asyncData.map(item => <li key={item.id}>{item.name}</li>)
        }
      </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

# todoList 练习示例

安装

// 创建项目
yarn create vite react-redux --template react
// 安装redux配套工具
yarn add  @reduxjs/toolkit react-redux
// 启动项目
yarn dev
1
2
3
4
5
6

新建store目录结构 src

  • store
    • modules // 模块store
    • index.js // 组合模块的入口文件
  • App.js

src/store/modules/todoListStore.js

import { createSlice } from '@reduxjs/toolkit'

const todoList = createSlice({
  // 模块名称独一无二
  name: 'todoList',
  // 初始化数据
  initialState: {
    list: [
      {
        name: 'react',
        id: 1,
        isCheck: true
      },
      {
        name: 'vue',
        id: 2,
        isCheck: false
      }
    ]
  },
  // 修改数据的同步方法
  reducers: {
    // 单选
    itemChange (state, action) {
      const id = action.payload
      const item = state.list.find(item => item.id === id)
      item.isCheck = !item.isCheck
    },

    // 全选
    allChange (state, action) {
      const isCheck = action.payload
      state.list.forEach(item => item.isCheck = isCheck)
    },

    // 删除
    itemDel (state, action) {
      const id = action.payload
      state.list = state.list.filter(item => item.id !== id)
    },

    // 添加新增内容
    addItem (state, action) {
      state.list.push(action.payload)
    }

  }
})


export const { itemChange, allChange, itemDel, addItem } = todoList.actions
const reducer = todoList.reducer
export default reducer


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

src/store/index.js

import { configureStore } from '@reduxjs/toolkit'

import todoListStore from './modules/todoListStore'

export default configureStore({
  reducer: {
    // 注册子模块
    todoListStore
  }
})
1
2
3
4
5
6
7
8
9
10

App入口提供 Store

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import store from './store'
import { Provider } from 'react-redux'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
)

1
2
3
4
5
6
7
8
9
10
11
12
13
14

组件中使用store

import './app.css'
import { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { itemChange, allChange, itemDel, addItem } from './store/modules/todoListStore'
function App () {
  const { list } = useSelector(state => state.todoListStore)
  const [inputValue, setInputValue] = useState('')
  // 发送修改数据
  const dispatch = useDispatch()
  // 单选
  const handleChange = (id) => {
    // action 
    const action = itemChange(id)
    // 提交action
    dispatch(action)
  }

  // 全选
  const allHandel = (e) => {
    console.log(e.target.checked)
    // action
    const action = allChange(e.target.checked)
    // 提交action
    dispatch(action)
  }

  // 删除
  const handelDel = (id) => {
    const action = itemDel(id)
    dispatch(action)
  }

  // 输入内容
  const handelInputChange = (e) => {
    setInputValue(e.target.value)
  }

  // 回车
  const handelKeyUp = (e) => {
    if (!inputValue.trim()) {
      return
    }
    if (e.keyCode === 13) {
      dispatch(addItem({
        name: inputValue,
        id: Math.random().toString().slice(-7),
        isCheck: false
      }))
      setInputValue('')
    }
  }

  return (
    <section className="todoapp">
      <header className="header">
        <h1>todos</h1>
        <input
          className="new-todo"
          autoFocus
          autoComplete="off"
          placeholder="What needs to be done?"
          value={inputValue}
          onChange={(e) => handelInputChange(e)}
          onKeyUp={(e) => handelKeyUp(e)}
        />
      </header>
      <section className="main">
        <input id="toggle-all" className="toggle-all" type="checkbox" checked={list.every(item => item.isCheck)} onChange={(e) => allHandel(e)} />
        <label htmlFor="toggle-all"></label>
        <ul className="todo-list">
          {
            list.map(item => (
              <li className={item.isCheck ? 'todo completed' : 'todo'} key={item.id}>
                <div className="view">
                  <input className="toggle" type="checkbox" onChange={() => handleChange(item.id)} checked={item.isCheck} />
                  <label>{item.name}</label>
                  <button className="destroy" onClick={() => handelDel(item.id)}></button>
                </div>
              </li>
            ))
          }
        </ul>
      </section>
    </section>
  )
}

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
更新: 8/22/2022, 1:56:52 PM