了解react-redux的使用方法
React-Redux是Redux的官方React绑定库。它能够使你的React组件从Redux store中读取数据,并且向store分发actions以更新数据。
详细信息请看文档
1 react-Redux将所有组件分成两大类
1.UI组件
(1)只负责 UI 的呈现,不带有任何业务逻辑
(2)通过props接收数据(一般数据和函数)
(3)不使用任何 Redux 的 API
(4)一般保存在components文件夹下
2.容器组件
(1)负责管理数据和业务逻辑,不负责UI的呈现
(2)使用 Redux 的 API
(3)一般保存在containers文件夹下
2 相关API
1.Provider:让所有组件都可以得到state数据
<Provider store={store}>
<App />
</Provider>
2.connect:用于包装 UI 组件生成容器组件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
3.mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性
const mapStateToProps = function(state){
return {count: state}
}
4.mapDispatchToProps:将分发action的函数转换为UI组件的标签属性
const mapDispatchToProps = function(dispatch){
return {
add:function(data){
//通知redux执行加法
dispatch(createAddAction(data))
},
sub: function(data){
dispatch(createSubAction(data))
},
addAsync: function(data,time){
dispatch(createAddAsyncAction(data,time))
}
}
}
3 计数器小案例
在这篇文章在React应用中使用 Redux使用了Redux进行实现计数器,本篇文章将利用react-redux进行修改简化。
使用react-redux进行修改
yarn add react-redux
在App.js中
import React, { Component } from 'react'
import Count from './containers/Count/Count.jsx'
import store from './redux/store'
export default class App extends Component {
render() {
return (
<div>
{/* 给容器组件传递store */}
<Count store = {store}></Count>
</div>
)
}
}
在src/components/Count/Count.jsx中
import React, { Component } from 'react';
class Count extends Component {
add = () => {
const {value} = this.selectNum
this.props.add(parseInt(value))
}
sub = () => {
const {value} = this.selectNum
this.props.sub(parseInt(value))
}
addOdd = () => {
const {value} = this.selectNum
if(this.props.count % 2 !==0){
this.props.add(parseInt(value))
}
}
addAsync = () => {
const {value} = this.selectNum
this.props.addAsync(parseInt(value),1000)
}
render() {
console.log('CountUI组件接收到的',this.props)
return (
<div>
<h1>当前值为:{this.props.count}</h1>
<select ref={(current)=>{this.selectNum = current}}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.add}>+</button>
<button onClick={this.sub}>-</button>
<button onClick={this.addOdd}>奇数+</button>
<button onClick={this.addAsync}>异步+</button>
</div>
);
}
}
export default Count;
在src/containers/Count/Count.jsx中
//引入Count UI组件
import CountUI from '../../components/Count/Count'
import {createAddAction,createSubAction,createAddAsyncAction} from '../../redux/count_action'
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'
//函数返回值作为状态传递给UI组件
const mapStateToProps = function(state){
return {count: state}
}
const mapDispatchToProps = function(dispatch){
return {
add:function(data){
//通知redux执行加法
dispatch(createAddAction(data))
},
sub: function(data){
dispatch(createSubAction(data))
},
addAsync: function(data,time){
dispatch(createAddAsyncAction(data,time))
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
4 进行优化代码
1.使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。
//index.js中不需要store.subscribe(()=>{})
ReactDOM.render(
<App />,
document.getElementById('root')
);
2.在index.js中修改为如下代码,无需自己给容器组件传递store,给App包裹一个Provider,然后传递store。这样,App的所有子组件就默认都可以拿到state了。
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import {Provider} from 'react-redux'
import store from './redux/store'
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
,
document.getElementById('root'));
reportWebVitals();
3.将容器组件和UI组件整合一个文件。可以将mapDispatchToProps,mapStateToProps进行简写。删除components文件,保留containers文件。
import React, { Component } from 'react';
import {createAddAction,createSubAction,createAddAsyncAction} from '../../redux/count_action'
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'
class Count extends Component {
add = () => {
const {value} = this.selectNum
this.props.add(parseInt(value))
}
sub = () => {
const {value} = this.selectNum
this.props.sub(parseInt(value))
}
addOdd = () => {
const {value} = this.selectNum
if(this.props.count % 2 !==0){
this.props.add(parseInt(value))
}
}
addAsync = () => {
const {value} = this.selectNum
this.props.addAsync(parseInt(value),1000)
}
render() {
console.log('CountUI组件接收到的',this.props)
return (
<div>
<h1>当前值为:{this.props.count}</h1>
<select ref={(current)=>{this.selectNum = current}}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.add}>+</button>
<button onClick={this.sub}>-</button>
<button onClick={this.addOdd}>奇数+</button>
<button onClick={this.addAsync}>异步+</button>
</div>
);
}
}
export default connect(
state=>({count: state}) //映射状态
,
{
add: createAddAction,
sub: createSubAction,
addAsync: createAddAsyncAction
//映射操作状态的方法
}
)(Count)
5 多组件数据共享
1.了解什么是combineReducers?
combineReducers(reducers):随着应用变得越来越复杂,可以考虑将 reducer 函数 拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分。
combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法。
合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 由 combineReducers() 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。
示例:
const allReducer = combineReducers({counts: countReducer,persons: personReducer})
// allReducer 将返回如下的 state 对象
{
counts: {
// ... counts, 和一些其他由 countReducer 管理的 state 对象 ...
},
persons: {
// ... persons, 和一些其他由 personReducer 管理的 state 对象 ...
}
}
通过为传入对象的 reducer 命名不同的 key 来控制返回 state key 的命名。例如,你可以调用 combineReducers({counts: countReducer,persons: personReducer}) 将 state 结构变为 { counts, persons }。
2.小案例:有一个Person组件,他可以显示数据person的name,age,并且也可以显示Count组件的count值。Count组件不仅可以显示count值,也可以显示Person组件的总人数。
(1)修改redux中的目录结构,添加actions,reducers文件夹,将组件对应的action,reducer分别放在对应的文件夹中,修改引入的路径。
(2)创建src/redux/actions/person.js,添加如下代码
export function addPerson (data){
return {
type: 'add_person',
data
}
}
(3)创建src/redux/reducers/person.js
const initPerson = [{id:'001',name:'xinxin',age:18}]
export default function personReducer(preState = initPerson,action){
const {type,data} = action
switch (type) {
case 'add_person':
return [data,...preState]
default:
return preState
}
}
(4)修改store.js。因为有多个reducer,所以需要引入combineReducers用来汇总所有的reducer。
import {createStore,applyMiddleware, combineReducers} from 'redux'
//引入为Count组件服务的reducer
import countReducer from '../redux/reducers/count_reducer'
//引入为Person服务的reducer
import personReducer from '../redux/reducers/person'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//汇总reducer
const allReducer = combineReducers({
counts: countReducer,
persons: personReducer
})
export default createStore(allReducer,applyMiddleware(thunk))
如果有多个reducer需要汇总,一般将创建一个单独的js文件,用于汇总reducer,不需要全部都写在store.js中。
(5)在src/containers/Person/Person.jsx添加如下代码
import React, { Component, Fragment } from 'react';
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'
import {addPerson} from '../../redux/actions/person.js'
import {nanoid} from 'nanoid'
class Person extends Component {
addPerson = () => {
const name = this.nameNode.value
const age = this.ageNode.value*1
const personObj = {
id: nanoid(), // 用于自动生成id
name,
age
}
console.log(personObj)
this.props.add(personObj)
}
render() {
console.log('Person组件接收到的',this.props)
return (
<Fragment>
<h1>我是Person组件,Count组件求和为:{this.props.count}</h1>
<input text="text" placeholder="请输入姓名" ref={cur => this.nameNode = cur}></input>
<input text="number" placeholder="请输入年龄" ref={cur => this.ageNode = cur}></input>
<br/>
<button onClick={this.addPerson}>添加</button>
<ul>
{
this.props.person.map((item)=>{
return <li key={item.id}>姓名为{item.name},年龄为{item.age}</li>
})
}
</ul>
</Fragment>
);
}
}
export default connect(
state=>({person: state.persons,count:state.counts}) //映射状态
,
{
add: addPerson,
//映射操作状态的方法
}
)(Person)
(6)修改src/containers/Count/Count.jsx代码
render() {
console.log('CountUI组件接收到的',this.props)
return (
<div>
<h1>Count组件当前值为:{this.props.count},Person组件总人数:{this.props.totalPerson}</h1>
<select ref={(current)=>{this.selectNum = current}}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.add}>+</button>
<button onClick={this.sub}>-</button>
<button onClick={this.addOdd}>奇数+</button>
<button onClick={this.addAsync}>异步+</button>
</div>
);
}
- 本文作者: étoile
- 版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!