React(九)- React数据共享和Redux案例最终完善
发布日期:2021-05-07 14:50:26 浏览次数:20 分类:技术文章

本文共 7784 字,大约阅读时间需要 25 分钟。

React(九)- React数据共享和Redux案例最终完善

前言

本篇文章是紧接着来讲解。上一篇文章中可以发现,案例都是以单组件为基础,那么接下来来看看如何利用Redux来实现多个组件之间的数据共享。

一. React数据共享

需求:写两个组件,每个组件能够实时的获得另一个组件的状态值,如图:

在这里插入图片描述
而两个组件之间,则通过Redux来进行数据共享。


1.1 案例:多组件之间数据共享

项目结构:

在这里插入图片描述
Count组件(整合UI和容器组件):

import React, {    Component } from 'react';// 引入connect用来连接UI组件和reduximport {    connect } from 'react-redux'import {       createIncrementAction,    createDecrementAction,    createIncrementAsyncAction} from '../../redux/actions/count'class Count extends Component {       increment = () => {           const {    value } = this.selectNumber        this.props.jia(value * 1)    }    decrement = () => {           const {    value } = this.selectNumber        this.props.jian(value * 1)    }    incrementIfOdd = () => {           const {    value } = this.selectNumber        if (this.props.count % 2 !== 0) {               this.props.jia(value * 1)        }    }    incrementAsync = () => {           const {    value } = this.selectNumber        this.props.jiaAsync(value * 1, 500)    }    render() {           return (            

我是Count组件,发现Person组件中的总人数为:{ this.props.renshu}

当前求和为:{ this.props.count}

  
  
  
  
); }}export default connect( // 此时要根据store文件中,对应的key来决定,因为我们有两个UI组件 // 因此在原本的基础上,我们在store文件中利用combineReducers将多个reducer进行了合并 // 返回的是一个对象,那么每个reducer都是以key,value的形式来存在,因此需要通过state.key来获得对应的状态 state => ({ count: state.he, renshu: state.rens.length }), { jia: createIncrementAction, jian: createDecrementAction, jiaAsync: createIncrementAsyncAction, })(Count)

Person组件(整合UI和容器组件):

import React, {    Component } from 'react'import {    nanoid } from 'nanoid'import {    connect } from 'react-redux'import {    createAddPersonAction } from '../../redux/actions/person'class Person extends Component {       addPerson = () => {           const name = this.nameNode.value        const age = this.ageNode.value        const personObj = {    id: nanoid(), name, age }        this.props.jiaYiRen(personObj)    }    render() {           return (            

我是Person组件,发现Count组件中的和为{ this.props.he}

this.nameNode = c} />
this.ageNode = c} />
    { this.props.yiduiren.map(p => { return
  • { p.name}---{ p.age}
  • }) }
) }}export default connect( state => ({ yiduiren: state.rens, he: state.he }), { jiaYiRen: createAddPersonAction }// 映射操作状态的方法)(Person)

store.js组件:

/** * 改文件专门用来暴露一个store对象,整个应用只有一个store对象 */// 引入createStore,专门用于创建redux中最为核心的store对象// combineReducers可以将多个reducer进行合并import {    applyMiddleware, createStore, combineReducers } from 'redux'// 引入为Count组件服务的reducerimport countReducer from './reducers/count'import personReducer from './reducers/person'import thunk from 'redux-thunk'// 合并所有的reducer,因为createStore()方法只有俩参数。const allReducer = combineReducers({       he: countReducer,    rens: personReducer,})// 暴露storeexport default createStore(allReducer, applyMiddleware(thunk))

常量文件constant.js

/** * 用于action对象中type类型的常量值 */ export const INCREMENT ='increment' export const DECREMENT ='decrement' export const ADD_PERSON ='addPerson'

App组件:

import React, {    Component } from 'react';import Count from './containers/Count'import Person from './containers/Person'class App extends Component {       render() {           return (            

); }}export default App;

入口文件index.js

import React from 'react'import ReactDOM from 'react-dom'import App from './App'import {    Provider } from 'react-redux'import store from './redux/store'ReactDOM.render(    
, document.getElementById('root'))

Action文件:

Action_Count.js文件:(项目中的文件名称为count.js,在actions目录下)

/** * 改文件专门为Count组件生成action对象*/import {    INCREMENT, DECREMENT } from '../constant'export const createIncrementAction = data => ({    type: INCREMENT, data })export const createDecrementAction = data => ({    type: DECREMENT, data })// 异步action就是指action的值为函数,异步action中一般会调用同步的action,// 需要npm install redux-thunk ,并配置在store中// 创建action的函数不再返回一般对象,而是一个函数,该函数中写一个异步任务// 异步任务有结果后,分发一个同步的action去真正操作数据export const createIncrementAsyncAction = (data, time) => {       return (dispatch) => {           setTimeout(() => {               dispatch(createIncrementAction(data))        }, time);    }}

Action_Person.js文件:(项目中的文件名称为person.js,在actions目录下)

import {    ADD_PERSON } from '../constant'// 创建增加一个人的action动作对象export const createAddPersonAction = personObj => ({    type: ADD_PERSON, data: personObj })

Reducer文件:

Reducer_Count.js文件:(项目中的文件名称为count.js,在reducers目录下)

/** * 1.该文件是用来创建一个为Count组件服务的Reducer,Reducer的本质就是一个函数 * 2.reducer函数会接收到两个参数,分别是:之前的状态preState,动作对象action */ import {    INCREMENT, DECREMENT } from '../constant' export default function countReducer(preState, action) {       if (preState === undefined) preState = 0    /**     * 1.type:标识属性,值为字符串,唯一,必要属性。       2.data:数据属性,值类型任意,可选属性。     */    const {    type, data } = action    // 根据type来决定如何加工数据    switch (type) {           case INCREMENT: // 如果是加            return preState + data        case DECREMENT: // 如果是减            return preState - data        default:            return preState    }}

Reducer_Person.js文件:(项目中的文件名称为person.js,在reducers目录下)

import {    ADD_PERSON } from '../constant'const initState = [{    id: '01', name: 'tom', age: 18 }]export default function personReducer(preState = initState, action) {       const {    type, data } = action    switch (type) {           case ADD_PERSON:            return [data, ...preState]        default:            return preState    }}

对于多个组件之间的数据共享问题,我们可以发现:

  • 多个reducerstore.js文件中都需要被暴露,但是createStore()方法只能暴露一个reducer,因此需要通过combineReducers()来合并reducer,并且每个reducer都需要为其定义一个key
  • 而合并之后的总状态是一个对象,而不再是一个单一的值。☆
  • 在组件中取出状态的时候,记得要根据对应的key来获取对应的状态。

1.2 纯函数

纯函数是一类特别的函数,只要是同样的输入(实参),必定得到同样的输出返回,同时必须遵守以下几点约束:

  • 不得改写参数数据。
  • 不会产生任何副作用,例如网络请求,输入和输出设备。
  • 不能调用Date.now()或者Math.random()等不纯的方法。
  • 而redux的reducer函数必须是一个纯函数。

因此,以下这种写法是错误的!:

Person(Reducer):

import {    ADD_PERSON } from '../constant'const initState = [{    id: '01', name: 'tom', age: 18 }]export default function personReducer(preState = initState, action) {       const {    type, data } = action    switch (type) {           case ADD_PERSON:             // 不可以这么写,因为会导致preState被改写,那么personReducer就不是纯函数了。            // preState.unshift(data)            // return preState            return [data, ...preState]        default:            return preState    }}

二. Redux案例最终版

store.js文件中,合并多个reducer的操作单独提出来,在reducers目录下创建一个index.js文件:

目录:
在这里插入图片描述

/** * 该文件用于汇总所有的reducer */import {    combineReducers } from 'redux'import countReducer from './count'import personReducer from './person'export default combineReducers({       he: countReducer,    rens: personReducer,})

那么store.js文件中更改为:

/** * 改文件专门用来暴露一个store对象,整个应用只有一个store对象 */// 引入createStore,专门用于创建redux中最为核心的store对象// combineReducers可以将多个reducer进行合并import {    applyMiddleware, createStore } from 'redux'// 引入为Count组件服务的reducerimport thunk from 'redux-thunk'// 只要是index为名字的文件,就可以省略不写import reducer from './reducers'// 暴露storeexport default createStore(reducer, applyMiddleware(thunk))

2.1 项目打包运行

打包命令如下:

npm run build

运行后,会生成一个build文件:里面包含了你这个项目中所用到的样式、Js等文件。

在这里插入图片描述
安装服务器:

npm i serve -g

启动命令:(在当前文件夹中)

serve build

执行效果如下:

在这里插入图片描述
页面能够正常显示:
在这里插入图片描述
此时这种运行处于发布模式:
在这里插入图片描述

上一篇:React(十)- React拓展:setState、lazyLoad以及Hook
下一篇:React(八)- ReactUI组件库及Redux的使用

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年04月14日 09时39分27秒