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