1. 为什么需要redux
-
组件之间的通信,数据流是单项的,顶层组件向下传递数据,通过【props】,底层组件是不可以向上层组件传递数据,如果底层组件想修改数据,只能通过顶层组件向下传递事件,底层组件触发事件,修改数据,当项目越来越大,组件之间的数据传递会比较繁琐。
-
【redux】在react中担任一个数据管理模块
-
【redux使用场景】
- 同一个【state】需要多个Component中进行共享
- 某一个组件的状态,需要进行共享
- 某一个状态需要在任何的地方都可以拿到
- 一个组件需要改变另一个组件的状态
- 一个组件需要改变全局状态
-
【redux】安装
npm install redux -S
npm install react-redux -S
- 【组件状态state选择那种方式进行管理】
- UI库相关的组件状态,在组件的内部进行保存
- 需要共享的【state】放在redux里面进行管理
- 像后端请求数据,放到【redux】里面进行管理
2. redux工作流程 待更新
3. store的创建
- 【store】是单独存在的,不存在于任何组建内,所以store在创建的时候,是单独放在一个文件内
【 redux 提供了一个函数,他用来生成store 】
import { createStore } from 'redux'
function reducer() { 【 创建一个reducer 】
return {
count: 0
}
}
const store = createStore(reducer) 【 createStore接收一个纯函数,也就是reducer 】
【 获取store里面的值 】
console.log(store.getState())
3.1 如何将store传递给组件,并组件获取使用
- 由于【 store 】与组件是分开的,并没有什么联系,所以想要进行数据传递,需要使用【react-redux】实现数据传递
【 src/index.js 】
import { Provider } from 'react-redux' 【 用导出的方法,将组件进行包裹,使用 】
ReactDOM.render(
<React.StrictMode>
<Provider store={ store }> <App /></Provider> 【 在Provider,属性=属性名的方式将store向下传递 】
</React.StrictMode>,
document.getElementById('root')
)
【 组件中获取store 】
import React from 'react'
import { connect } from 'react-redux' 【 使用 connect方法获取数据】
function App (props) { 【 03. 组件要通过props来获取store里面的数据 】
return (
<div>{props.count}</div>
)
}
/******获取数据*******/
【 02. connect的回调函数接受一个参数state,也就是store传递过来的数据,store里面的数据来源于reducer 】
const mapStateToProps = (state) => ({
count: state.count
})
/*********************/
【 01. connect调用两次,第一次调用是拿到数据,第二次调用是要讲数据传给哪一个组件 】
export default connect(mapStateToProps)(App)
3.2 更改store数据(action)
- 【 action 】本身是一个对象类型,它里面有有一个【type】属性,他是一个字符串类型,他类似一个自定义事件
- 组件通过获取【store】,接受到store里面包含数据以及一个方法,该方法是【dispath】,它用于触发提交【action】,组件通过触发dispath,然后【reducer】接收到【action】,并进行操作
【 组件内部 】
import React from 'react'
import { connect } from 'react-redux'
function App (props) {
console.log(props);
return (
<div>
<span>{props.count}</span>
【 dispath的内部参数就是action 】
<button onClick={() => {props.dispatch({ type: 'button' })}}>点击按钮</button>
</div>
)
}
const mapStoreToState = (state) => ({
count: state.count
})
export default connect(mapStoreToState)(App)
【 reducer 】
const item = {
count: 0
}
const reducer =(state = item , action) => {
if ( action.type === 'button' ) {
return {
count: state.count += 1
}
}else {
return {
count: state.count
}
}
}
export default reducer
3.3 action的第二个参数
- action的第二个参数是【 content 】,当组件触发dispatch的时候,想给【reducer】传递数据,这时候需要给action设置第二个参数
【 组件传递 】
<button onClick={() => {props.dispatch({ type: 'button',payload: 5 })}}>点击按钮</button>
【 reducer收到 】
const reducer =(state = item , action) => {
console.log(action.payload)
}
4. 简化action
- 在组件内部,在元素身上去调用action,一是不方便,二是书写的长度比较长,所以将action提取成函数
- 通过使用【connect】的第二个参数,实现将action提取成函数
import React from 'react'
import { connect } from 'react-redux'
function App (props) {
console.log(props);
return (
<div>
<span>{props.count}</span>
<button onClick={() => {props.bts({payload:5})}}>点击按钮</button> 【 在使用的时候,connect会生成好方法,可以通过props去调用方法 】
</div>
)
}
const mapStoreToState = (state) => ({
count: state.count
})
const mapStoreDispatch = (dispatch) => ({
btn() {
dispatch({type: 'button'}) 【 无参数 】
},
bts(payload) {
dispatch({type: 'button',...payload}) 【 有参数 】
}
})
export default connect(mapStoreToState,mapStoreDispatch)(App)
- 在进行简化,将【 action 】放在单独的文件内部,结合【redux】里面的【bindActionCreators】
【 action单独存放在文件内部 】
export const BTN = () =>({ type: 'button' }) 【 无参数 】
export const BTNs = (payload) =>({ type: 'button',payload }) 【 有参数 】
【 组件里 】
import { bindActionCreators } from 'redux'
import React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { BTN } from './action'
function App (props) {
console.log(props);
return (
<div>
<span>{props.count}</span>
<button onClick={() => {props.BTN()}}>点击按钮</button>
</div>
)
}
const mapStoreToState = (state) => ({
count: state.count
})
const mapStoreDispatch = (dispatch) => ({
...bindActionCreators({BTN},dispatch)
})
export default connect(mapStoreToState,mapStoreDispatch)(App)
-
【bindActionCreators】通过使用redux内部的方法,设置自动生成action触发函数,
- 方法是内部接收两个参数,参数一是一个对象,参数二是dispatch
- bindActionCreators({ },dispatch)
4.1 action常量设置
- 将type的值拿出来定义成常量,原因是因为这个type的值在多个地方进行使用,也是避免在之后书写时,没有语法提示,容易出错,所以将type类型的值定成常量
【 action_type.js 】
export const INCREMENT ='increment'
export const INCREMENT_N ='increment_n'
【 reducer内部引入使用 】
import { INCREMENT,INCREMENT_N } from './action_type.js'
const item = {
count: 0
}
const reducer =(state = item , action) => {
if (action.type === INCREMENT) {
return {
count: state.count+=1
}
}
}
export default reducer
【 action定义的地方引入 】
import { INCREMENT,INCREMENT_N } from './action_type.js'
export const BTN = () =>({ type: 'INCREMENT' }) 【 无参数 】
5. reducer的拆分与合并
- 【reducer】的类型是一个纯函数类型,而【reducer】是处理数据的地方,返回他返回的结果会传递【store】,进行保存
- 【 reducer 】他与【store】是单独存放的,并且他们是分开存放的【 Store/reducer/index.js 】
【 多个reducer进行合并 redux里面的combinerReducers方法 】
import { combinerReducers } from 'redux'
import contentReducer from './content.reducer.js' /* reducer文件
import payloadReducer from './content.reducer.js' */
export default combinerReducers({
content: contentReducer,
payload: payloadReducer
})
【combinerReducers返回的数据格式 { counter: {count},person:[{}] }】
【 index.js 里面的引入需要进行更改 】
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux'
import reducer from './reducer/index.js' 【 将合并后的reducer导入进来,传入store里面 】
import { createStore } from 'redux'
const store = createStore(reducer)
ReactDOM.render(
<React.StrictMode>
<Provider store={store}><App /></Provider>
</React.StrictMode>,
document.getElementById('root')
);
【 组件中获取,通过connect第一个参数获取想要拿到的数据 】
const mapStoreToState = (state) => ({ 【 state打印的结果是一个对象组成的,通过打点,拿到想要的数据即可 】
count: state.content
})
6. redux中间件
6.1 添加中间件后redux的工作流程
6.2 中间件的使用
- 【为什么要使用中间件】比如说在项目开发过程中,数据不是同步获取的,或者是在处理数据之前有一些异步操作需要完成,这时,通过action发送的事件传动到中间件,让中间件进行处理,然后在返回给store,进行使用。
- 【什么时候会触发中间件】,当组件Components触发【action】的时候,就会去执行中间件
- 【注册中间件 applyMiddleware 】applyMiddleware是一个方法
【 index.js 】
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux'
import { createStore,applyMiddleware } from 'redux'
function reducer( state,action ) {
return state
}
// 定义一个中间件函数
function middle() {
// 在内部,规定需要返回一个函数
return function () {
// 异步处理函数,需要返回一个函数
return function () {
// 这里就是,当action被触发的时候,触发,此处是完成异步操作
}
}
}
【 applyMiddleware注册一个中间件,想要哪一个函数成为中间件,就需要将函数传给applyMiddleware,完成注册 】
const store = createStore(reducer,applyMiddleware(middle))
ReactDOM.render(
<React.StrictMode>
<Provider store={store}><App /></Provider>
</React.StrictMode>,
document.getElementById('root')
);
6.3 redux-saga异步解决方案
- 下载【npm install redux-saga】
- 【redux-saga】基本使用
【 index.js 】
import { createStore,applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga' 【 createSagaMiddleware进行调用,返回值才能作为中间件 】
import loadSaga from './store/saga.js' 【 处理过的saga导入到index.js 里面】
const sagaMiddleware = createSagaMiddleware()
const store = createStore(toList,applyMiddleware(sagaMiddleware))
sagaMiddleware.run(loadSaga)
【 saga如何截取action 】
import { takeEvery, put } from 'redux-saga/effects' 【 redux-saga提供了两个方法 】
1. takeEvery 截取action指令
2. put 相当于dispatch,向后重新发送一个指令
【 由于redux-saga 的底层是基于es6中的generator 】
function* loadPerson () { 【 同为generator语法 】
let data = yield axios.get() // 发送一个请求,拿到请求的数据data
yield put({ type: 'load_success',data }) 【 请求成功发送请求,再次触发一个action,将数据传递给reducer 】
}
export default function* personSaga () {
【 需要了解的是,takeEvery拦截一个指令,然后要在重新创建一个函数,来处理拦截之后要做的事情 】
yield takeEvery( 'load_person', loadPerson )
}
【 reducer里面的接收action,完成一些操作 】
6.4 redux-saga的合并
【 redux-saga/effects 下的all方法 】
import { all } from 'redux-saga/effects'
import personSage from './person.saga'
export default function* rootSaga () {
yield all([
personSage() //多个文件直接逗号进行书写即可
])
}
6.5 redux-saga基本使用练习
【 组件 】
【 action 】
【 reducer 】
【 saga 】
【 index.js 】
6.6 action与reducer的优化
- 通过下载【redux-actions】包,来优化action与reducer 【 npm i redux-actions 】
【 action的优化 】
import { createAction } from 'redux-actions'
export const increment_action = createAction('increment_action')【 createAction返回的是action信息对象 】
【 reduce的优化 】
// 如果需要和当前配置高名相同,可以改方法起别名进行使用
import { handleActions } from 'redux-action' 【 handleAction as countReducer 别名的方式 】
import { increment_action } from 'action配置路径' 【 action指令 】
const initText = { 【 初始化数据 】
const :10
}
1. handleActions呦两个参数,第一个参数是对象,里面是键值对
2. 第二个参数是初始化数据
const createReducer = handleActions({
[increment_action]: () => ({ 【 键 接收的是哪一个指令,值就是当触发指令要做什么,是一个函数 】
【 函数呦两个参数,第一个参数当前初始化数据state,
第二个参数是action,方便指令传参使用
】
})
},initText)
export default createReducer