Reudx
# Redux
状态管理工具
React
全家桶
react
核心 -react hooks
react-router
(react-router-dom)- 状态管理 - mobx(简单)/ redux(复杂)
- 中间件:
redux-thunk
、redux-saga
# redux基本概念
核心概念 :store
、action
、reducer
store
:仓库(存储数据), 管理action
和reducer
action
:表示一个动作或者行为的抽象reducer
:根据action
来对数据加工返回新数据
# action
actions
是任务的抽象,试图中的每个用户交互都是一个 action
,比如:添加任务、删除任务、登录、加入购物车灯
- 本质上是一个 js 普通对象
action
内必须使用一个字符串类型type
的唯一标识action
如果数量过多,需要采用模块化的方式管理
1. action是一个js对象
2. action必须提供type属性,表示动作的类型
3. type属性的值是一个字符串,采用全大写字母表示,多个单词使用_连接
{
type: "INCREMENT"
}
4. action中除了type,还可以指定动作需要的其他数据
{
type: "ADD_TODO",
todoName: '学习redux'
}
5. 将来要完成的所有功能,都抽象成了一个个的动作
2
3
4
5
6
7
8
9
10
11
12
# action-creator
用来创建 action
的函数,直接使用对象来创建 action
不灵活,参数写死. 一般使用函数来创建 action
,一般把创建 action
的函数叫做 actionCreator
- action creator创建函数只是简单的返回一个 action
- action creator创建函数的好处是更容易被移植和测试
const delTodo = (id) => ({ type: 'DEL_TODO', id })
console.log(delTodo(5))
2
# reducer
reducer
是一个 纯函数
,接收旧的 state
和 action
,返回新的 state
reducer
的state
需要有一个默认值,这个默认值就是redux
的初始值reducer
内部需要判断action
的type
,根据type
进行处理,处理的时候不能修改原来的数据educer
如果遇到不认识的type
,一定要返回原来的state
(重要)
示例
function reducer(state = 0, action) {
switch (action.type) {
case 'ADD':
return state + 1
case 'MINUS':
return state - 1
default:
return state
}
}
2
3
4
5
6
7
8
9
10
纯函数
特点:只要是固定的输入,必定是固定的输出
原则:
不能改写参数
不能调用
Date.now()
等函数不能使用全局变量
没有副作用
# store
- getState
- dispatch
- subscribe 订阅
store
是把 action
、reducer
联系到一起的对象
// store
const { createStore } = window.Redux
// 参数1: reducer
const store = createStore(reducer)
2
3
4
通过
getState()
方法可以获取到state
数据document.querySelector('span').innerHTML = store.getState()
1通过
dispatch(action)
方法可以派遣任务更新state
数据document.querySelector('.add').onclick = function () { store.dispatch(add()) }
1
2
3通过
subscribe(listener)
方法可以订阅数据的更新// 订阅:只要state发生了变化,这个订阅的回调函数就会执行。 // 返回值:调用了就会取消订阅 const unsubscribe = store.subscribe(() => { console.log(store.getState()) document.querySelector('span').innerHTML =store.getState() }) unsubscribe()//停止监听
1
2
3
4
5
6
7
# react-redux
以上 redux
的使用和 react
是相互独立的 ,要配合 react
的使用需要使用到 react-redux
包
react-redux
redux-devtools-extension
redux-thunk
# 不使用react-redux的使用
action.js
export const add = (count) => {
return {
type: 'ADD',
count,
}
}
2
3
4
5
6
reducers.js
export const addReducer = (
state = {
count: 1,
},
action
) => {
switch (action.type) {
case 'ADD':
return Object.assign({}, state, { count: action.count })
default:
return state
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
store\index.js
import ReactDOM from 'react-dom'
import App from './App.js'
import { createStore } from 'redux'
import { addReducer } from "./store/reducers"
let store = createStore(addReducer)
ReactDOM.render(<App store={store}></App>, document.querySelector('#root'))
store.subscribe(() => {
ReactDOM.render(<App store={store}></App>, document.querySelector('#root'))
})
2
3
4
5
6
7
8
9
App.js
import { add } from './store/actions'
const App = (props) => {
const {
store: { getState, dispatch },
} = props
const { count } = getState()
return (
<div>
<h3>App</h3>
<p>{count}</p>
<button
onClick={() => {
dispatch(add(2))
}}
>
点我+2
</button>
</div>
)
}
export default App
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 使用 react-redux 包
store.js
// 创建store
import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer)
export default store
2
3
4
5
6
reducers.js
function reducer(state = 100, action) {
console.log(action)
if (action.type === 'ADD') return state + 1
if (action.type === 'MINUS') return state - 1
return state
}
export default reducer
2
3
4
5
6
7
8
9
App.js
// react和redux没有关系
import Child from './Child'
export default function App() {
return (
<div>
<h1>根组件</h1>
<div>我被点击了0次</div>
<hr />
<Child></Child>
</div>
)
}
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 App from './App'
import { Provider } from 'react-redux'
import store from './store'
/*
react-redux的使用步骤
1. 安装 yarn add react-redux
2. 导入Provider组件 并且使用Provider组件包裹App组件
3. 在任意的子组件中都可以获取到store
*/
ReactDom.render(
<Provider store={store}>
<App></App>
</Provider>,
document.getElementById('root')
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Child.js
import { useSelector, useDispatch } from 'react-redux'
import { add } from './store/action'
export default function Child() {
// 调用useStore就可以得到store对象
// 调用useSelector 得到 store中数据
// const store = useStore()
const count = useSelector((state) => state)
const dispatch = useDispatch()
return (
<div>
<h3>我是子组件---点击了{count}次</h3>
<button onClick={() => dispatch(add())}>修改</button>
</div>
)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
重点步骤
- 使用
<Provider></Provider>
包裹<App>
根组件为所有子组件提供 store useStore
获取store
对象useSelector
获取state
数据useDispatch
获取派遣任务的函数
# redux处理异步
redux-thunk
import {createStore,applyMiddleware} from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'
const store = createStore(reducer,applyMiddleware(thunk))
export default store
2
3
4
5
配置
//如果是同步的 action , 直接返回一个 action 对象
//如果是异步的 aciton , 需要返回一个异步的函数 , 在异步函数中,处理完之后 , 需要在异步函数中 dispatch
export const buy = (money) =>{
return {
type:'BUY',
money,
}
}
export const buyAsync = (money) =>{
return (dispatch)=>{
setTimeout(()=>{dispatch(buy(money))},3000)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
使用
<button onClick={()=>dispatch(buyAsync(300))}></button>
# 分离变量名
- 新建
action_type.js
文件 - 在文件中
const ADD_MONEY = 'ADD_MONEY'
声明变量 export
导出import {}
导入- 使用更加方便管理
# 多个reducer合并
combineReducers(reducers)
在 reducers
文件下有多个 reducer
需要导入给 store
使用 ,需要使用到这个合并的 API .
a
function user(state = {name:'y',age:20},action){
return state
}
2
3
b
function money(state = 100,action){
return state
}
2
3
合并
const rootReducer = combineReducers({
a:user,
b:money
})
export default rootReducer
2
3
4
5
使用
const money = useSelector((state)=>state.money)
const user= useSelector((state)=>state.user)
# redux开发工具
- 浏览器安装
react-redux
开发工具 - 安装和导入
redux-devtools-extension
包
使用
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))
2
3
配置好后可以在浏览器中打开