系列文章目录
第一章:React基础知识(React基本使用、JSX语法、React模块化与组件化)(一)
第二章:React基础知识(组件实例三大核心属性state、props、refs)(二)
第三章:React基础知识(事件处理、受控组件与非受控组件、高阶函数、组件的生命周期)(三)
第四章:React脚手架应用(创建脚手架、代理配置、ajax相关、组件通信)(四)
第五章:react-router5路由相关一(路由相关概念、基本使用、NavLink与NavLink的封装、Switch的使用、严格匹配、路由重定向、路由组件与一般组件的区别)(五)
第六章:react-router5路由相关二(嵌套路由、路由传参、replace、编程式路由导航、withRouter的使用、BrowserRouter与HashRouter的区别)(六)
第七章:React-Router6路由相关一(路由的基本使用、重定向、NavLink·、路由表、嵌套路由)(七)
第八章:React-Router6路由相关二(路由传参、编程式路由导航、路由相关hooks)(八)
第九章:React相关扩展一(setState、lazyLoad、Hooks相关)(九)
第十章:React相关扩展二(Fragment、Content、组件优化、render props、错误边界)(十)
第十一章:Redux相关知识(什么是redux、redux的工作原理、redux的核心概念、redux的基本使用)(十一)
第十二章:React-Redux相关知识(什么是react-redux、react-redux的原理、react-redux相关API、react-redux的基本使用)(十二)
文章目录
如果还不会redux建议看完第十一章后再来看这章,这样会更好理解
一、什么是react-redux
1.1 概念
Ract-Redux
是Redux的官方React绑定库
,它能够使你的React组件从Redux store中读取数据,并且向store分发actions以更新数据。用redux 库来创建 store , 利用 react-redux 库来获取 store 中的数据或者更新数据。
- react-redux提供了两个常用的 api ,一个是:
Provider
,一个是:connect
。组件之间共享的数据是Provider这个顶层组件通过props传递下去的,store必须作为参数放到Provider组件中去。而connect则提供了组件获取 store中数据或者更新数据的接口。- 学习网站: https://react-redux.js.org/
1.2 react-redux的原理
React-Redux 将所有组件分成两大类: UI 组件
(负责 UI 的呈现)和容器组件
(负责管理数据和逻辑)。
-
UI组件
只负责 UI 的呈现,不带有任何业务逻辑
- 没有状态(即不使用this.state这个变量)
- 所有
数据都由参数(this.props)提供
不使用任何 Redux 的 API
- 因为不含有状态,UI 组件又称为"纯组件",即它跟纯函数一样,纯粹由参数决定它的值。
-
容器组件
负责管理数据和业务逻辑,不负责 UI 的呈现
- 带有内部状态
使用 Redux 的 API
-
UI 组件和容器组件的结合
- 如果一个组件既有 UI 又有业务逻辑,那么将它拆分成两层结构:
外面是一个容器组件
,里面包了一个UI组件
。前者负责与外部的通信
,将数据传给后者,由后者渲染出视图
。 - React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux自动生成。
- 如果一个组件既有 UI 又有业务逻辑,那么将它拆分成两层结构:
二、相关API
react-redux在7.1版本之前使用connect函数配合Provider来进行操作。以下介绍7.1版本之前的Provider及connect函数。
2.1 <Provider>
组件
概念:connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。
React-Redux 提供Provider组件,可以让容器组件拿到state。
用法:在根组件外面包了一层Provider,App的所有子组件都可以拿到state了
。它的原理是React组件的context属性,store放在了上下文对象context上面。React-Redux自动生成的容器组件的代码,就类似下面这样,然后子组件就可以从context拿到store。
示例代码
import store from './redux/store'
// src/index.js
<Provider store={store}>
<App />
</Provider>
2.2 connect()
React-Redux中的connect()方法用于从 UI 组件生成容器组件
,connect(mapStateToProps?,
mapDispatchToProps?, mergeProps?, options?) 。之后就可以在该组件的props中获取到仓库中数据和dispatch方法。
export default connect(mapStateToProps, mapDispatchToProps)(ConponentUI);
2.3 mapStateToProps()
mapStateToProps
字面含义是把state映射到props中去
,意思就是把Redux中的数据映射到React中的props中去。
- mapStateToProps是一个函数。它的作用是
建立一个从外部state对象到 UI 组件的props对象的映射关系
。执行后返回一个对象,
里面的每一个键值对就是一个映射。 - mapStateToProps会订阅(绑定) Store,每
当state更新的时候,就会自动执行
,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。 - mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。使用ownProps作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。
- 如果connect方法省略mapStateToProps参数,那么UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。
// 映射仓库中的数据到props中
const mapStateToProps = (state, ownProps) => {
return {
inputValue: state.inputValue,
test: 'hello',
listData: state.listData
}
}
2.4 mapDispatchToProps()
mapStateToProps 字面含义是把dispatch映射到props中去,意思就是把Redux中的修改数据的方法映射到React中的props中去。
- mapDispatchToProps是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了用户的哪些操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。
- 如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。
- 如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。
// 映射仓库中的dispatch到props中
const mapDispatchToProps = (dispatch, ownProps) => {
return {
inputChange: (e) => {
// 获取input数据,更改store中的数据
let action = {
type: 'TO_CHANGE_INPUT',
value: e.target.value
};
// 分发action
dispatch(action);
},
// 添加
toAdd: () => {
let action = {
type: 'TO_ADD'
};
dispatch(action);
},
// 删除
toDelete: (index) => {
// console.log(index);
let action = {
type: 'TO_DELETE',
index
};
dispatch(action);
},
// 完成
toChangeStatus: (index) => {
let action = {
type: 'TO_CHANGE_STATUS',
index
};
dispatch(action);
},
}
}
三、react-redux的基本使用
-
安装
npm install react-redux --save # 或 yarn add react-redux
-
代码案例片段:
index.js
import React from 'react' import ReactDOM from 'react-dom' import App from './App' import store from './redux/store' import {Provider} from 'react-redux' ReactDOM.render( <Provider store={store}> <App/> </Provider>, document.getElementById('root') )
App.js
import React, { Component } from 'react' import Count from './containers/Count' //引入的Count的容器组件 import Person from './containers/Person' //引入的Person的容器组件 export default class App extends Component { render() { return ( <div> <Count/> <hr/> <Person/> </div> ) } }
redux/action/count.js(该文件专门为Count组件生成action对象)
//同步action,就是指action的值为Object类型的一般对象 export const createIncrementAction = data => ({type:'increment',data}) export const createDecrementAction = data => ({type:'decrement',data}) //异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。 export const createIncrementAsyncAction = (data,time) => { return (dispatch)=>{ setTimeout(()=>{ dispatch(createIncrementAction(data)) },time) } }
redux/action/person.js(该文件专门为Person组件生成action对象)
//创建增加一个人的action动作对象 export const addPerson = personObj => ({type:'add_person',data:personObj})
redux/reducer/count.js
/* 1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数 2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action) */ const initState = { value:0,index:1 } //初始化状态 export default function countReducer (preState = initState, action) { console.log('preState:',preState); console.log('action:',action); //从action对象中获取:type、data const { type, data } = action //根据type决定如何加工数据 switch (type) { case 'increment': //如果是加 return { ...preState, value: preState.value + data } case 'decrement': //若果是减 return { ...preState, value: preState.value - data } default: return preState } }
redux/reducer/person.js
//初始化人的列表 const initState = [{id:'001',name:'tom',age:18}] export default function personReducer(preState=initState,action){ // console.log('personReducer@#@#@#'); const {type,data} = action switch (type) { case 'add_person': //若是添加一个人 //preState.unshift(data) //此处不可以这样写,这样会导致preState被改写了,personReducer就不是纯函数了。 return [data,...preState] default: return preState } }
redux/reducer/index.js
/* 该文件用于汇总所有的reducer为一个总的reducer */ //引入combineReducers,用于汇总多个reducer import {combineReducers} from 'redux' //引入为Count组件服务的reducer import count from './count' //引入为Person组件服务的reducer import persons from './person' //汇总所有的reducer变为一个总的reducer export default combineReducers({ count, persons })
redux/store.js
/* 该文件专门用于暴露一个store对象,整个应用只有一个store对象 */ //引入createStore,专门用于创建redux中最为核心的store对象 // 旧版 // import { createStore } from 'redux' //新版 import { legacy_createStore as createStore, applyMiddleware } from 'redux' //引入汇总之后的reducer import reducer from './reducers' //引入redux-thunk,用于支持异步action import thunk from 'redux-thunk' //引入redux-devtools-extension import {composeWithDevTools} from 'redux-devtools-extension' //暴露store export default createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))
container/Count.jsx
import React, { Component } from "react"; //引入action import { increment, decrement, incrementAsync, } from "../../redux/actions/count"; //引入connect用于连接UI组件与redux import { connect } from "react-redux"; //定义UI组件 class Count extends Component { state = { carName: "奔驰c63" }; //加法 increment = () => { const { value } = this.selectNumber; this.props.increment(value * 1); }; //减法 decrement = () => { const { value } = this.selectNumber; this.props.decrement(value * 1); }; //奇数再加 incrementIfOdd = () => { const { value } = this.selectNumber; if (this.props.count % 2 !== 0) { this.props.increment(value * 1); } }; //异步加 incrementAsync = () => { const { value } = this.selectNumber; this.props.incrementAsync(value * 1, 500); }; render() { //console.log('UI组件接收到的props是',this.props); return ( <div> <h2>我是Count组件,下方组件总人数为:{this.props.renshu}</h2> <h4>当前求和为:{this.props.count}</h4> <select ref={(c) => (this.selectNumber = c)}> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button onClick={this.increment}>+</button> <button onClick={this.decrement}>-</button> <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> <button onClick={this.incrementAsync}>异步加</button> </div> ); } } //使用connect()()创建并暴露一个Count的容器组件 //写法一 /* //1.mapStateToProps函数返回的是一个对象; //2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value //3.mapStateToProps用于传递状态 function mapStateToProps(state){ return {count:state.count.value} } //1.mapDispatchToProps函数返回的是一个对象; //2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value //3.mapDispatchToProps用于传递操作状态的方法 function mapDispatchToProps(dispatch){ return { jia:number => dispatch(createIncrementAction(number)), jian:number => dispatch(createDecrementAction(number)), jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)), } } //使用connect()()创建并暴露一个Count的容器组件 export default connect(mapStateToProps,mapDispatchToProps)(Count) */ //写法二 /* export default connect( state => ({count:state.count.value}), //mapDispatchToProps的一般写法 //dispatch => ({ // jia:number => dispatch(createIncrementAction(number)), // jian:number => dispatch(createDecrementAction(number)), // jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)), // }) //mapDispatchToProps的简写 { jia:createIncrementAction, jian:createDecrementAction, jiaAsync:createIncrementAsyncAction, } )(Count) */ //写法三(推荐) //使用connect()()创建并暴露一个Count的容器组件 export default connect( (state) => ({ count: state.count.value, personCount: state.persons.length, }), { increment, decrement, incrementAsync } )(Count);
container/Person.jsx
import React, { Component } from 'react' import {nanoid} from 'nanoid' import {connect} from 'react-redux' import {addPerson} from '../../redux/actions/person' class Person extends Component { addPerson = ()=>{ const name = this.nameNode.value const age = this.ageNode.value*1 const personObj = {id:nanoid(),name,age} this.props.addPerson(personObj) this.nameNode.value = '' this.ageNode.value = '' } render() { return ( <div> <h2>我是Person组件,上方组件求和为{this.props.count}</h2> <input ref={c=>this.nameNode = c} type="text" placeholder="输入名字"/> <input ref={c=>this.ageNode = c} type="text" placeholder="输入年龄"/> <button onClick={this.addPerson}>添加</button> <ul> { this.props.persons.map((p)=>{ return <li key={p.id}>{p.name}--{p.age}</li> }) } </ul> </div> ) } } export default connect( state => ({ persons:state.persons, count:state.count.value }),//映射状态 {addPerson}//映射操作状态的方法 )(Person)
运行结果:
持续更新~~~