1.redux核心
1.1什么是redux?
redux是javascript状态容器,提供可预测化的状态管理。
这些状态最终我们都会把它抽象成数据,保存在一个对象中。这个对象就被称之为状态容器。所谓状态容器就是一个javascript对象。在javascript保存了一些数据,这些数据和页面当中的dom元素的状态是一一对应的。
有了状态容器有什么好处呢?
以前操作dom元素都是通过document.getElementById找到这个dom元素,然后再去操作它。有了状态容器我们就不用去页面中查找dom元素,我们直接操作dom元素对于的状态对象就可以了。可以试想一下,是操作dom方便,还是操作js对象方便?肯定是直接操作js对象方便。
页面当中的元素是怎么发生变化呢?
这部分就交给框架来做,例如:react,vue都有自己内部的机制,把状态同步到dom元素中。
提供可预测化的状态管理,什么意思?
在大型项目中,糟糕的状态管理会让项目的维护成本增加。这个时候就希望有一个科学的状态管理的方式。redux的出现就是为了解决这个问题。在redux中提供了一种科学的状态管理方式。通过这个可科学的状态管理方式,当状态发生变化,这个状态变的可以预测。【可以预测】对我们有什么好处?当项目中发生状态的问题时,我们可以很容易定位到这个问题出现在哪里。
1.2 redux 的核心概念和工作流程
1. redux 的核心概念
:
store
:存储状态的容器,是一个javascript对象。(在redux的应用中,要求我们把所有的状态都存储到store这个对象中)
reducer
:是一个函数,这个函数的作用:就是用来向store中存储状态以及更新store中的状态。(也就是说:reducer中返回什么store中就存储什么。)
action
:作用是:用来描述store中的状态进行怎样的操作。(是一个javascript对象,需要一个固定的属性,这个属性叫做type,这个type的属性值是一个字符串,这个由开发人员自己定。)
view
: 视图,指的就是html页面。
- 工作流程
2. 工作流程描述
:
由于所有的状态都存粗在store中,又由于view就是一个视图,他不能直接操作store,所以视图想更改store中的状态,它必须先通过dispatch方法触发一个action,(描述对当前的状态进行怎样的操作)这个action会被reducer接收到,reducer或根据action中的type属性的不同,对状态进行不同的处理,当reducer把这个状态处理完成以后,再通过返回值的方式返回给store,更新store值的状态,当store中的状态被更新之后,通过subscribe方法,就能知道这个状态更新了,然后再去同步视图中的状态。这个就是他的工作流程。
1.3.如何使用redux
首先看下store里面到底有什么?
- 获取store对象存储的状态。我们发现store里面返回的都是一些方法。
- getState: 获取store中的数据
- dispatch: 触发action
- subscribe:订阅store中的数据变化。参数是一个函数。(得到最新的状态,同步视图)
// 1创建store。
var store = Redux.createStore(reducer)
// 获取store对象存储的状态。我们发现store里面返回的都是一些方法。
// getState: 获取store中的数据
// dispatch: 触发action
// subscribe:订阅store中的数据变化。参数是一个函数。(得到最新的状态,同步视图)
console.log(store)
案例:计数器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="plus"> +</button>
<button id="minus"> -</button>
<button id="count">0</button>
<script src= "redux.min.js"> </script>
<script>
// 1创建store。
// 默认状态的存储有2种方式:1.createStore中可以传递第2个参数(默认值,但是一般不这样传递初始化的值。)2 一般直接放在reducer函数的的参数中赋值
var store = Redux.createStore(reducer)
// 3. 存储默认的状态
var inisialState = {
count: 0
}
// 2.创建reducer函数。
function reducer(state = inisialState, action) {
switch(action.type) {
case 'increment' :
return {
count: state.count + 1
}
case 'decrement' :
return {
count: state.count -1
}
default :
return state
}
}
// 4.定义action。数值+1,数值-1
var increment = {
type: 'increment'
}
var decrement = {
type: 'decrement'
}
// 5.添加点击事件
document.getElementById('plus').onclick = function() {
// 6.触发action
store.dispatch(increment)
}
document.getElementById('minus').onclick = function() {
store.dispatch(decrement)
}
// 7. 订阅store。
store.subscribe(() => {
// 8.store中的数据同步到页面中。
// store.getState() 可以看到当点击按钮的时候,数据已经发生了变化。
document.getElementById('count').innerHTML = state.count
})
// 获取store对象存储的状态。我们发现store里面返回的都是一些方法。getState, dispatch
console.log(store.getState())
</script>
</body>
</html>
1.4.redux核心api
2.react+redux
2.1 在react中不使用redux时遇到的问题。
2.2 在react中加入redux的好处
2.3 如何在react中使用redux
npm install create-react-app -g
安装:npm install redux react-redux
创建项目: create-react-app guide
react-redux
:提供了2个方法:
1. Provider
: 将创建出来的store放在全局位置2. connect
:
1. 在connect方法的内部会帮助我们订阅store,当store中的状态发生变化之后,会帮助我们重新渲染组件。
2. 通过connect可以拿到store中的状态,将状态通过组件的props属性映射给组件。
3. 可以获取到dispatch方法3. connect参数
:
第一个参数:mapStateToProps。是一个函数。这个函数的返回一个对象,这个对象中写什么,props就会获取到什么。props中会带有dispatch方法。
第2个参数:mapDispatchToProps。是一个函数,可以拿到dispatch方法。
2.3.1. 实现计数器案例(react-redux):
Provider组件与connect方法的实践:
import React from 'react'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
const initialState = {
count: 0
}
// 创建reducer
function reducer(state = initialState, action) {
switch(action.type) {
case 'increment':
return {
state: state.count + 1
}
case 'decrement':
return {
state: state.count - 1
}
default:
return state
}
return state
}
// 创建store
const store = createStore(reducer)
// 通过provider将store放在全局的组件可以获取到的地方
React.renderDOM( <Provider store = {store}></Provider> , document.getElementById('root'))
组件Counter.js
// Counter.js
import React from 'react'
import { connect } from "react-redux"
// function Counter(props) {
// 可以看到打印的props中有一个dispatch方法
// console.log(props)
function Counter({count, increment, decrement}) {
return (
<div>
<button onClick={()=>increment()}>+</button>
<button onClick={()=>decrement()}>-</button>
<button>{count}</button>
</div>
)
}
/**
* connect参数:
* 第一个参数:是一个函数。这个函数的返回一个对象,这个对象中写什么,props就会获取到什么。props中会带有dispatch方法。
* 第2个参数:是一个函数,可以拿到dispatch方法。
*/
const mapStateToProps = (state) => ({
count: state.count
})
const mapDispatchToProps = (dispatch) => ({
increment(){
dispatch({type: 'increment'})
},
decrement() {
dispatch({type: 'decrement'})
},
})
export default connect(mapStateToProps)(Counter)
在函数式组件中:打印:获取到props,带有dispatch方法:如下图:
2.3.2. bindActionsCreators方法的使用:
./actions/counter.action.js
// /actions/counter.action.js
export const increment = () => {
return {type: 'increment'}
}
export const decrement = () => {
return {type: 'decrement'}
}
Counter.js
// Counter.js
import React from 'react'
import { connect } from "react-redux"
import { bindActionCreators } from 'redux'
import * as counterActions from './actions/counter.action.js'
function Counter({count, increment, decrement}) {
return (
<div>
<button onClick={()=>increment()}>+</button>
<button onClick={()=>decrement()}>-</button>
<button>{count}</button>
</div>
)
}
/**
* connect参数:
* 第一个参数:是一个函数。这个函数的返回一个对象,这个对象中写什么,props就会获取到什么。props中会带有dispatch方法。
* 第2个参数:是一个函数,可以拿到dispatch方法。
*/
const mapStateToProps = (state) => ({
count: state.count
})
//bindActionCreators 参数解析:
// 第一个参数: 是一个对象。 第2个参数:dispatch。
// 返回值:是一个对象
const mapDispatchToProps = (dispatch) => bindActionCreators(counterActions, dispatch)
export default connect(mapStateToProps)(Counter)
2.3.3. 代码重构:
store目录结构:
action/counter.action.js
import { INCREMENT, DECREMENT } from "../const/counter.const";
export const increment = payload => ({type: INCREMENT, payload});
export const decrement = payload => ({type: DECREMENT, payload});
const/counter.const.js
// 抽象成常量。因为常量写的时候会有代码提示,避免我们写错代码单词。字符串没有提示。
// 是一个优化操作
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
reducer/counter.reducer.js
import { INCREMENT, DECREMENT } from "../const/counter.const";
const initialState = {
count: 0
}
export default (state = initialState, action) => {
switch(action.type) {
case INCREMENT:
return {
...state,
count: state.count + 1
}
case DECREMENT:
return {
...state,
count: state.count - 1
}
default:
return state;
}
}
component/Counter.js
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as couterActions from '../store/actions/counter.actions';
function Counter ({count, increment, decrement}) {
return <div>
<button onClick={() => increment()}>+</button>
<span>{count}</span>
<button onClick={() => decrement()}>-</button>
</div>
}
// 1. connect 方法会帮助我们订阅store 当store中的状态发生更改的时候 会帮助我们重新渲染组件
// 2. connect 方法可以让我们获取store中的状态 将状态通过组件的props属性映射给组件
// 3. connect 方法可以让我们获取 dispatch 方法
const mapStateToProps = state => ({
count: state.counter.count
});
const mapDispatchToProps = dispatch => bindActionCreators(couterActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
2.3.4. Action传递参数:
修改代码:
component/Counter.js
传递参数
:
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as couterActions from '../store/actions/counter.actions';
function Counter ({count, increment, decrement}) {
return <div>
+ <button onClick={() => increment(5)}>+</button>
<span>{count}</span>
+ <button onClick={() => decrement(5)}>-</button>
</div>
}
// 1. connect 方法会帮助我们订阅store 当store中的状态发生更改的时候 会帮助我们重新渲染组件
// 2. connect 方法可以让我们获取store中的状态 将状态通过组件的props属性映射给组件
// 3. connect 方法可以让我们获取 dispatch 方法
const mapStateToProps = state => ({
count: state.counter.count
});
const mapDispatchToProps = dispatch => bindActionCreators(couterActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
action/counter.action.js
接收参数
import { INCREMENT, DECREMENT } from "../const/counter.const";
// action接收参数
+ export const increment = payload => ({type: INCREMENT, payload});
+ export const decrement = payload => ({type: DECREMENT, payload});
reducer修改参数
import { INCREMENT, DECREMENT } from "../const/counter.const";
const initialState = {
count: 0
}
export default (state = initialState, action) => {
switch(action.type) {
case INCREMENT:
return {
...state,
+ count: state.count + action.payload
}
case DECREMENT:
return {
...state,
+ count: state.count - action.payload
}
default:
return state;
}
}
2.3.5. 案例: redux实现弹出框案例:
components/modal
import React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux';
import * as modalActions from '../store/actions/modal.actions';
function Modal({stateShow, show, hide}) {
const style ={
width: 200,
height: 200,
position:'absolute',
left: '50%',
top: '50%',
marginLeft: -100,
marginTop: -100,
background: 'blue',
display: stateShow ? 'block' : 'none'
}
return (
<div>
<button onClick={show}>显示</button>
<button onClick={hide}>隐藏</button>
<div style={style}></div>
</div>
)
}
const mapStateToProps = (state) => ({
stateShow: state.show
})
const mapDispatchToProps = dispatch => bindActionCreators(modalActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Modal)
store/action/modal.actions.js
import { HIDEMODAL, SHOWMODAL } from '../const/modal.const'
export const show = () => ({type: 'SHOWMODAL'})
export const hide = () => ({type: 'HIDEMODAL'})
store/const/modal.const.js
export const SHOWMODAL = 'showModal'
export const HIDEMODAL = 'hideModal'
store/reducer.js
import { INCREMENT, DECREMENT } from "../const/counter.const";
import { HIDEMODAL, SHOWMODAL } from "../const/modal.const";
const initialState = {
count: 0,
+ show: false
}
export default (state = initialState, action) => {
switch(action.type) {
case INCREMENT:
return {
...state,
count: state.count + action.payload
}
case DECREMENT:
return {
...state,
count: state.count - action.payload
}
+ case SHOWMODAL:
+ return {
+ ...state,
+ show: true
+ }
+ case HIDEMODAL:
+ return {
+ ...state,
+ show: false
+ }
default:
return state;
}
}
2.3.6 拆分合并reducer
- 要合并reducer,需要借助redux提供的方法:combineReducers
新建文件:store/reducer/root.reducer.js
- 用来存放所有的reducer
// 要合并reducer,需要借助combineReducers
import { combineReducers } from 'redux'
import counterReducer from './counter.reducer'
import modalReducer from './modal.reducer'
// store中的状态 :{counter: {count: 0}, modal:{show:false}}
export combineReducers({
counter: counterReducer,
modal: modalReducer
})
modal组件的reducer单独写一个文件:modal.reducer.js。
新建文件:store/reducer/modal.reducer.js
import { HIDEMODAL, SHOWMODAL } from "../const/modal.const";
const initialState = {
show: false
}
export default (state = initialState, action) => {
switch(action.type) {
case SHOWMODAL:
return {
...state,
show: true
}
case HIDEMODAL:
return {
...state,
show: false
}
default:
return state;
}
}
store/reducer/counter.reducer.js
删除之前在counter.reducer.js中的有关modal的代码。
import { INCREMENT, DECREMENT } from "../const/counter.const";
const initialState = {
count: 0,
}
export default (state = initialState, action) => {
switch(action.type) {
case INCREMENT:
return {
...state,
count: state.count + action.payload
}
case DECREMENT:
return {
...state,
count: state.count - action.payload
}
default:
return state;
}
}
components/modal
修改reducer在代码中的使用
import React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux';
import * as modalActions from '../store/actions/modal.actions';
function Modal({stateShow, show, hide}) {
const style ={
width: 200,
height: 200,
position:'absolute',
left: '50%',
top: '50%',
marginLeft: -100,
marginTop: -100,
background: 'blue',
display: stateShow ? 'block' : 'none'
}
return (
<div>
<button onClick={show}>显示</button>
<button onClick={hide}>隐藏</button>
<div style={style}></div>
</div>
)
}
const mapStateToProps = (state) => ({
+ stateShow: state.modal.show
})
const mapDispatchToProps = dispatch => bindActionCreators(modalActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Modal)
store/index.js
store中引入的reducer修改为combineReducers合并的的reducer。
import { createStore } from "redux"
+ import RootReducer from './reducers/root.reducer'
+ export const store = createStore(RootReducer)
3.redux中间件
1. 什么是中间件?
中间件:本质是一个函数。redux允许我们通过中间件的方式扩展redux的应用程序。体现在action的处理上。
之间action是直接被reducer处理的。加入了中间件之后:当组件去触发一个action之后,这个action会优先被中间件处理,当中间件处理完这个action之后,中间件把这个action传递给reducer,reducer继续处理这个action。
2. 在加入中间件以后的的redux的工作流程:
组件去触发一个action,这个action被store接收后以后,这个store优先去调用了中间件(middleware)函数去处理action,并且把接收到的action传递给中间件,在这个中间件之后可以对action进行处理,当中间件处理完这个action 之后,会把action 传递给reducer,让reducer去处理这个action。这就是加入了中间件的redux工作流程。
它提供的是位于 action 被发起之后,到达 reducer 之前的扩展点。 你可以利用 Redux middleware 来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。
4.开发redux中间件
Redux中间件是一个函数,科里化的函数。
4.1. 开发中间件的模版:
export default store => next => action => {}
/*
通过store.getState()获取当前的状态
通过store.dispatch触发action
action:组件触发的action对象。
next:是一个函数,当中间件的逻辑处理完成之后,调用next,调用next的作用是把这个action传递给reducer,或者传递给下一个中间件。
中间件可以有多个。
*/
4. 2. 注册中间件
中间件在开发完成以后只有被注册才能在Redux的⼯作流程中⽣效。
redux中有一个方法:applyMiddleware,用来注册中间件。
案例:
import { createStore, applyMiddleware } from 'redux'
import logger from './middlewares/logger'
createStore(reducer,applyMiddleware(logger) )
4.3.自己实现中间件:
案例:logger中间件:作用:打印action和store的日志。
4.3.1 新建文件夹 store/middleware
1.store/middleware/logger.js
/*
中间件是一个科里化函数
export default function (store) {
return function(next) {
return function(action) {
}
}
}
*/
// 写成如下的箭头函数
/*
参数分析:
第1个参数store
store的返回值:getState(), diapatch()
第2个参数:
next函数。执行完之后必须调用next方法,不调用代码就卡在这里,action就不能传递给下一个中间件或者是reducer。action作为next的参数
第3个参数:
action对象/函数。如果是同步任务:action是一个对象,如果是异步任务,action是函数,这个函数中必须传递dispatch方法。
action:具体要执行的任务。
*/
export default (store) => (next) => (action) => {
console.log(store)
console.log(action)
// action作为next的参数
next(action)
}
4.3.2. 使用applyMiddleware注册中间件
store/index.js
import { createStore, applyMiddleware } from "redux"
import RootReducer from './reducers/root.reducer'
+ import logger from './middleware/logger'
+ import thunk from './middleware/thunk'
// 中间件的执行顺序:取决于注册顺序。
+ export const store = createStore(RootReducer, applyMiddleware(logger, thunk))
查看效果:直接看console控制台,输出的内容。
4.3.3实现thunk中间件:可以执行异步操作:
实现thunk中间件:可以执行异步操作:
- 新建文件:store/middleware/thunk.js
//thunk.js
// 如何区分同步操作/异步操作?
// 如果是同步操作action就传递一个对象,如果是异步操作就传递函数。
// 异步操作代码就写在你传递进来的函数中。当前这个中间件函数在调用你传递进来的函数时,要将dispatch方法传递过去。
// export default (store) => (next) => (action) => {
export default ({dispatch}) => (next) => (action) => {
if (typeof action === 'function') {
return action(dispatch)
}
next(action)
}
在action中使用
import { INCREMENT, DECREMENT } from "../const/counter.const";
// action结收参数
export const increment = payload => ({type: INCREMENT, payload});
export const decrement = payload => ({type: DECREMENT, payload});
/*
异步action:返回一个函数。这个函数中带有dispatch方法。这个dispatch是thunk中间件传递的
payload:是传递的参数
export const increment_async = (payload) => {
return function (dispatch) => {}
}
*/
// 写成箭头函数如下
export const increment_async = (payload) => (dispatch) => {
setTimeout(() => {
dispatch(increment(payload))
}, 2000)
}
4.3.4 常用的redux的中间件(thunk, saga):
redux- thunk
-
- redux-thunk npm install redux-thunk --save
-
- 使用:redux-thunk
-
- 注册redux-shunk
store/index.js
+ import { createStore, applyMiddleware } from "redux"
import RootReducer from './reducers/root.reducer'
+ import thunk from 'redux-thunk'
// 中间件的执行顺序:取决于注册顺序。
+ export const store = createStore(RootReducer, applyMiddleware(thunk))
-
- 使用redux-thunk中间件
// 可以直接拿到dispatch。
// 当异步操作完成之后,我们触发另外的action,这个action帮助我们把这个异步操作的结果传递给reducer,reducer对数据进行更新。
const loadPosts = () => async (dispatch) => {
const posts = await axios.get('/api/posts').then(response => response.data)
dispatch({type: LOADPOSTSSUCCESS, payload: posts})
}
redux-saga
redux-saga 解决的问题:可以将异步操作从action creator文件中抽离出来,放在一个单独的文件中。
- 安装: npm install redux-saga --save
- 创建redux-sags中间件
import createSagaMiddleware from 'redux-saga'
const sagaMiddleware = createSagaMiddleware()
- 注册sagsMiddleware
createStore(reducer, applyMiddleware(sagaMiddleware))
- 使用saga接收action,执行异步操作
/*
takeEvery方法的作用:接收action,当组件触发一个action的时候,在saga文件中可以通过takeEvery这个方法去接收这个action.
put:用来触发另外一个action,当异步操作返回结果以后,我们需要通过put方法去触发一个action,帮助我们把这个异步操作的结果传递给reducer,让reducer把这个数据保存到store中。put方法的作用和dispatch的方法是一样的。
在saga文件中要求我们默认导出一个generator函数。所以在文件的底部通过export default 关键字导出一个函数,在generator这个函数中需要通过takeEvery接收这个action。在调用takeEvery这个方法前面需要加上yield这个关键字。
takeEvery这个方法的参数:1.第一个参数是:action,action的类型字符串,而不是函数。第2个参数是:要执行的方法的名字或者是函数。在这个方法中执行异步操作。
*/
import {takeEvery, put} from './store/saga/post.saga'
function * load_posts() {
const { data } = yield axios.get('/api/post.json')
yield put(load_posts_success(data))
}
export default function * postSaga() {
yield takeEvery(LOAD_POSTS, load_posts)
}
- 启动saga。调用sagaMiddleware.run启用saga。这样我们写的saga才会加入到redux的工作流程中。
import postSaga from './store/saga/post.saga.js'
sagaMiddleware.run(postSaga)
Redux-saga中间件的使用案例:计算器
- 新建文件夹 store/sagas
书写counter.saga.js
store/sagas/counter.saga.js
// store/sagas/counter.saga.js
import { takeEvery, put, delay } from 'redux-saga/effects'
import { increment } from '../actions/counter.actions'
import { INCREMENT_ASYNC } from '../const/counter.const'
// takeEvery:接收action
// put:触发action
// delay:延迟接收的时间
export default function * counterSaga() {
yield takeEvery(INCREMENT_ASYNC, increment_async_fn)
}
function * increment_async_fn() {
// 延迟2秒钟
yield delay(2000)
// 调用同步代码
yield put(increment())
}
- 书写action
store/actions/counter.actions.js
import { INCREMENT, DECREMENT, INCREMENT_ASYNC } from "../const/counter.const";
export const increment = payload => ({type: INCREMENT, payload});
export const decrement = payload => ({type: DECREMENT, payload});
// saga
+ export const increment_async = () => ({type: INCREMENT_ASYNC})
store/const/counter.const.js
// 抽象成常量。因为常量写的时候会有代码提示,避免我们写错代码单词。字符串没有提示。
// 是一个优化操作
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
+ export const INCREMENT_ASYNC = 'increment_async'
- 在组件中使用:Counter
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as couterActions from '../store/actions/counter.actions';
function Counter ({count, increment, decrement, increment_async}) {
return <div>
+ <button onClick={() => increment_async()}>+</button>
<span>{count}</span>
<button onClick={() => decrement(5)}>-</button>
</div>
}
// 1. connect 方法会帮助我们订阅store 当store中的状态发生更改的时候 会帮助我们重新渲染组件
// 2. connect 方法可以让我们获取store中的状态 将状态通过组件的props属性映射给组件
// 3. connect 方法可以让我们获取 dispatch 方法
const mapStateToProps = state => ({
count: state.counter.count
});
const mapDispatchToProps = dispatch => bindActionCreators(couterActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
Redux-saga中的action传参:
Counter组件
```javascript
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as couterActions from '../store/actions/counter.actions';
function Counter ({count, increment, decrement, increment_async}) {
return <div>
+ <button onClick={() => increment_async(20)}>+</button>
<span>{count}</span>
<button onClick={() => decrement(5)}>-</button>
</div>
}
// 1. connect 方法会帮助我们订阅store 当store中的状态发生更改的时候 会帮助我们重新渲染组件
// 2. connect 方法可以让我们获取store中的状态 将状态通过组件的props属性映射给组件
// 3. connect 方法可以让我们获取 dispatch 方法
const mapStateToProps = state => ({
count: state.counter.count
});
const mapDispatchToProps = dispatch => bindActionCreators(couterActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
store/actions/counter.actions.js
import { INCREMENT, DECREMENT, INCREMENT_ASYNC } from "../const/counter.const";
export const increment = payload => ({type: INCREMENT, payload});
export const decrement = payload => ({type: DECREMENT, payload});
// saga
+ export const increment_async = (payload) => ({type: INCREMENT_ASYNC, payload})
store/sagas/counter.saga.js
// store/sagas/counter.saga.js
import { takeEvery, put, delay } from 'redux-saga/effects'
import { increment } from '../actions/counter.actions'
import { INCREMENT_ASYNC } from '../const/counter.const'
// takeEvery:接收action
// put:触发action
// delay:延迟接收的时间
export default function * counterSaga() {
// 注意:takeEvery的第一个参数是字符串,不是action。第2个参数是:函数名称。不加 ().
yield takeEvery(INCREMENT_ASYNC, increment_async_fn)
}
+ function * increment_async_fn(action) {
// 延迟2秒钟
yield delay(2000)
// 调用同步代码
+ yield put(increment(action.payload))
}
saga文件的拆分与合并
1.新建文件夹 store/sagas
单个saga文件
store/sagas/counter.saga.js
import { takeEvery, put, delay } from 'redux-saga/effects';
import { increment } from '../actions/counter.actions';
import { INCREMENT_ASYNC } from '../const/counter.const';
// takeEvery 接收 action
// put 触发 action
function* increment_async_fn (action) {
yield delay(2000);
yield put(increment(action.payload))
}
export default function* counterSaga () {
// 接收action
yield takeEvery(INCREMENT_ASYNC, increment_async_fn)
}
单个saga文件
store/sagas/modal.saga.js
import { takeEvery, put, delay } from 'redux-saga/effects';
import { SHOWMODAL_ASYNC } from '../const/modal.const';
import { show } from '../actions/modal.actions';
function* showModal_async_fn () {
yield delay(2000);
yield put(show());
}
export default function* modalSaga () {
yield takeEvery(SHOWMODAL_ASYNC, showModal_async_fn)
}
合并saga
store/sagas/root.saga.js
import { all } from 'redux-saga/effects';
import counterSaga from './counter.saga';
import modalSaga from './modal.saga';
export default function* rootSaga () {
yield all([
counterSaga(),
modalSaga()
])
}
store/index.js
import { createStore, applyMiddleware } from "redux";
import RootReducer from "./reducers/root.reducer";
import createSagaMidddleware from 'redux-saga';
import rootSaga from './sagas/root.saga';
// 1.创建 sagaMiddleware
const sagaMiddleware = createSagaMidddleware();
export const store = createStore(RootReducer, applyMiddleware(sagaMiddleware));
// 启动 counterSaga
sagaMiddleware.run(rootSaga)
redux-actions常用中间件
redux-actions:解决的问题:版主我们简化了action和reducer的处理。
- 安装 npm install redux-actions --save
- 使用
- 创建action
store/action/counter.action.js
// createAction: 帮助我们生成action函数
// 当传递参数时,createAction('increment'),会自动帮你添加到action的payload上。直接传直接用。
import {createAction} from 'rredux-actions'
export const increment_action = createAction('increment') // 传递的参数是一个字符串,也就是action中的type属性。
export const decrement_action = createAction('decrement')
- 创建reducer
store/reducer/counter.reducer.js
// 给handleAction这个方法取一个别名createReducer
import { handleAction as createReducer } from 'redux-actions'
import { increment_action, decrement_action } from '../action/counter.action'
// 初始状态
const initialState = {
count: 0
}
// createReducer的返回值:就是一个reducer
// 参数解析:1。第一个参数是对象,对象里面是函数,这些函数用来处理state 2.第2个参数是初始值
const counterReducer = createReducer({
[increment_action]: (state, action) => ({count: state.count + action.payload}),
[decrement_action]: (state, action) => {
return state.count - action.payload
},
}, initialState)
export default counterReducer
components/Counter.js
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as couterActions from '../store/action/counter.action';
function Counter ({count, increment, decrement_action, increment_action}) {
return <div>
<button onClick={() => increment_action(5)}>+</button>
<span>{count}</span>
<button onClick={() => decrement_action(5)}>-</button>
</div>
}
const mapStateToProps = state => ({
count: state.counter.count
})
const mapDispatchToProps = dispatch => bindActionCreators(couterActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
5.redux综合案例
6.redux Toolkit
对redux的二次封装,用于高效的redux的开发,使redux的使用变的简单。
yarn add @redux/toolkit@1.6.0 react-redux@7.2.4 --save
。当前是没没有单独的安装redux,因为redux/toolkit内部封装的有redux。
6.1. 创建状态切片
对于状态切片,我们可以认为它就是原本redux中的那一个个小的reducer函数。
在redux中,原本reducer函数和action对象需要分别创建,现在通过创建切片替代,它会返回reducer函数和action对象。
1.创建状态切片
// createSlice这个方法,返回两个函数 1. reducer函数 2. actions是一个对象,在这个对象中会存储多个action creator
import {createSlice} from '@reduxjs/toolkit'
const {reducer: TodosReducer,actions} = createSlice()
export const {addTodo} = actions
export default TodosReducer
import {createSlice} from '@reduxjs/toolkit'
// 配置createSlice
// todos.slice.js
const {reducer: TodosReducer,actions} = createSlice({
+ name: TODOS_FEATURE_KEY, // 配置的唯一标识
+ initialState: [], // 初始状态
+ reducers: { // reducers的值是一个对像。里面是reducer函数,函数中默认有参数state,action。state就是状态,action就是通过dispatch方法触发的函数
+ addTodo:(state, action)=>{ // addTodo类似于switch case
+ state.push(action.payload) // action.payload中的payload是工具集自动帮助我们添加的,值就是触发action传递的参数
+ }
+ }
})
export const TODOS_FEATURE_KEY ='todos'
export const {addTodo} = actions
export default TodosReducer
- 创建store
// index.js
//configureStore作用:创建store
import { configureStore } from '@reduxjs/toolkit'
import TodosReducer, { TODOS_FEATURE_KEY } from './todos.slice'
export default configureStore({
// reducer作用:合并reducer
reducer: {
//格式: [状态的名字]:对应的reducer
// TODOS_FEATURE_KEY:状态的名字
// TodosReducer:对应的reducer
[TODOS_FEATURE_KEY]: TodosReducer
},
// 是否开启调试模式
devTools: process.env.NODE_ENV !== 'production'
})
3.配置Provider
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
+ import { Provider } from 'react-redux'
+ import store from './store/index'
ReactDOM.render(
+ <Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
+ </Provider>,
document.getElementById('root')
- 在组件中触发 Action,获取状态
案例:todolist
- Action预处理
当 Action 被触发后, 可以通过 prepare 方法对 Action 进行预处理, 处理完成后交给 Reducer, prepare 方法必须返
回对象.
- 执行异步操作(方法一)
-
- 创建执行异步操作的 Action 创建函数
createAsyncThunk作用: 创建执行异步操作的 Action 创建函数。
createAsyncThunk第一个参数:action.对象中的type属性值。是一个字符串。
第2个参数:是一个函数,异步操作放到这个函数中,有2个参数,payload,thunkApi,
thunkApi:就是redux-toolkit提供的一些api,例如dispatch,就是存储在thunkApi中。
- 创建执行异步操作的 Action 创建函数
-
- 创建接收异步操作结果的 Reducer
- 创建接收异步操作结果的 Reducer
-
- 在组件中触发 Action
- 在组件中触发 Action
- 执行异步操作(方法二)
-
- 创建执行异步操作的 Action 创建函数
- 创建执行异步操作的 Action 创建函数
-
- 创建接收异步操作结果的 Reducer
- 创建接收异步操作结果的 Reducer
- 配置中间件 - redux-logger
npm i redux-logger
getDefaultMiddleware:获取内置的中间件。返回值是一个数组。
index.js
- 实体适配器
实体可以理解为数据
实体适配器 :可以理解为:放入数据的容器。
将状态放入实体适配器 ,实体适配器提供操作状态的各种方法,简化操作。
import { createEntityAdapter } from '@reduxjs/toolkit'
const todosAdapter = createEntityAdapter()
todosAdapter.getInitialState()
// addOne: 向实体适配器中添加一条数据
// addMay: 向实体适配器中添加多条数据
todosAdapter.addOne(state, action.payload)
todosAdapter.addMay(state, action.payload)