React(九)

阅读: 评论:0

React(九)

React(九)

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

  • 前言
  • 一. React数据共享
    • 1.1 案例:多组件之间数据共享
    • 1.2 纯函数
  • 二. Redux案例最终版
    • 2.1 项目打包运行

React系列文章导航

前言

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

一. React数据共享

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

而两个组件之间,则通过Redux来进行数据共享。


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

项目结构:

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

import React, { Component } from 'react';
// 引入connect用来连接UI组件和redux
import { connect } from 'react-redux'import {createIncrementAction,createDecrementAction,createIncrementAsyncAction
} from '../../redux/actions/count'class Count extends Component {increment = () => {const { value } = this.selectNumberthis.props.jia(value * 1)}decrement = () => {const { value } = this.selectNumberthis.props.jian(value * 1)}incrementIfOdd = () => {const { value } = this.selectNumberif (unt % 2 !== 0) {this.props.jia(value * 1)}}incrementAsync = () => {const { value } = this.selectNumberthis.props.jiaAsync(value * 1, 500)}render() {return (<div><h1>我是Count组件,发现Person组件中的总人数为:{shu}</h1><h3>当前求和为:{unt}</h3><select ref={c => this.selectNumber = c}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select>&nbsp;&nbsp;<button onClick={this.increment}>+</button>&nbsp;&nbsp;<button onClick={this.decrement}>-</button>&nbsp;&nbsp;<button onClick={this.incrementIfOdd}>当前和为奇数时,才能够相加</button>&nbsp;&nbsp;<button onClick={this.incrementAsync}>异步加</button></div>);}
}export default connect(// 此时要根据store文件中,对应的key来决定,因为我们有两个UI组件// 因此在原本的基础上,我们在store文件中利用combineReducers将多个reducer进行了合并// 返回的是一个对象,那么每个reducer都是以key,value的形式来存在,因此需要通过state.key来获得对应的状态state => ({ count: state.he, renshu: s.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.valueconst age = this.ageNode.valueconst personObj = { id: nanoid(), name, age }this.props.jiaYiRen(personObj)}render() {return (<div><h1>我是Person组件,发现Count组件中的和为{this.props.he}</h1><input type="text" placeholder="输入名字" ref={c => this.nameNode = c} /><input type="text" placeholder="输入年龄" ref={c => this.ageNode = c} /><button onClick={this.addPerson}>添加</button><ul>{this.props.yiduiren.map(p => {return <li key={p.id}>{p.name}---{p.age}</li>})}</ul></div>)}
}export default connect(state => ({ yiduiren: s, he: state.he }),{ jiaYiRen: createAddPersonAction }// 映射操作状态的方法
)(Person)

store.js组件:

/*** 改文件专门用来暴露一个store对象,整个应用只有一个store对象*/
// 引入createStore,专门用于创建redux中最为核心的store对象
// combineReducers可以将多个reducer进行合并
import { applyMiddleware, createStore, combineReducers } from 'redux'
// 引入为Count组件服务的reducer
import countReducer from './reducers/count'
import personReducer from './reducers/person'import thunk from 'redux-thunk'
// 合并所有的reducer,因为createStore()方法只有俩参数。
const allReducer = combineReducers({he: countReducer,rens: personReducer,
})
// 暴露store
export 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 (<div><Count /><hr /><Person /></div>);}
}
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'
der(<Provider store={store}><App /></Provider>, ElementById('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 + datacase DECREMENT: // 如果是减return preState - datadefault: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 } = actionswitch (type) {case ADD_PERSON:return [data, ...preState]default:return preState}
}

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

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

1.2 纯函数

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

  • 不得改写参数数据。
  • 不会产生任何副作用,例如网络请求,输入和输出设备。
  • 不能调用或者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 } = actionswitch (type) {case ADD_PERSON: // 不可以这么写,因为会导致preState被改写,那么personReducer就不是纯函数了。// preState.unshift(data)// return preStatereturn [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组件服务的reducer
import thunk from 'redux-thunk'
// 只要是index为名字的文件,就可以省略不写
import reducer from './reducers'// 暴露store
export default createStore(reducer, applyMiddleware(thunk))

2.1 项目打包运行

打包命令如下:

npm run build

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

安装服务器:

npm i serve -g

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

serve build

执行效果如下:

页面能够正常显示:

此时这种运行处于发布模式:

本文发布于:2024-01-30 12:46:09,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170658997520093.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:React
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23