React+Redux基本使用流程
Redux简介
Redux是React生态中重要的组成部分。React提出将展示组件与容器组件分离的思想,降低了React与Redux之间的耦合度。
Store
:整个应用的数据存储中心,集中大部分页面需要的状态数据;ActionCreators
:view 层与data层的介质;Reducer
:接收action并更新Store。
所以流程是用户通过界面组件触发ActionCreator,携带Store中的旧State与Action流向Reducer,Reducer返回新的state,并更新界面。
案例实现
依赖库
yarn add prop-types
yarn add react-redux
yarn add redux
yarn add redux-thunk
页面组件
这是一个纯React代码 ,结构清晰
component/person.js
import React, {Component} from 'react';
import PropTypes from 'prop-types'
class Person extends Component {
//声明属性
static propTypes = {
name: PropTypes.string.isRequired,
nameCreator: PropTypes.func.isRequired,
age: PropTypes.number.isRequired,
ageCreator: PropTypes.func.isRequired,
nameAsync: PropTypes.func.isRequired
}
//点击事件
handlerNameFunc = () => {
const inputName = this.refs.inputValueName.value;
this.props.nameCreator(inputName);
}
handlerAgeFunc = () => {
const inputAge = this.refs.inputValueAge.value;
this.props.ageCreator(inputAge);
}
handlerAsyncFunc = () => {
const inputName = this.refs.inputValueName.value;
this.props.nameAsync(inputName);
}
//渲染界面
render() {
const {name, age} = this.props;
return (
<div>
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<label> {name} </label><br/>
<input ref="inputValueName"/>
<button onClick={this.handlerNameFunc}>Name Confirm</button>
<button onClick={this.handlerAsyncFunc}>Async Confirm</button>
<br/>
<label> {age} </label><br/>
<input ref="inputValueAge"/>
<button onClick={this.handlerAgeFunc}>Age Confirm</button>
<br/>
<br/>
</div>
);
}
}
export default Person;
ActionCreator
redux/action-type.js
ActionCreator会传达用户的操作信息以及一些数据。定义一些常量供我们使用,这里就两种操作,一是添加名字,二是添加年龄,实际都一样。为了后面实现reducer的合并强行写了俩。这些常量一般都定义在actionTpye文件中。
export const NAME = 'NAME'
export const AGE = 'AGE'
redux/actions.js
接着就是ActionCreator ,定义了一些操作类型,告诉store自己是干什么的,需要什么样的数据。
import {NAME, AGE} from "./action-type";
//包含所有的action creator
export const nameCreator = (name) => ({type: NAME, data: name})
export const ageCreator = (age) => ({type: AGE, data: age})
export const nameAsync = (name) => {
return dispatch => {
setTimeout(() => {
dispatch(nameCreator(name))
}, 2000);
}
}
redux/reducers.js
Reducer会接收到action的信息。然后进行状态(数据)的处理,相当于react中的setState()
的功能。如果有多个reducer ,可以使用combineReducers
方法将其合并,并暴露出去。
//包含n个reducer函数的模块
import {NAME, AGE} from './action-type'
import {combineReducers} from 'redux'
function name(state = 'initRedux', action) { //形参默认值
switch (action.type) {
case NAME:
return action.data
default:
return state
}
}
function age(state = 0, action) {
switch (action.type) {
case AGE:
return action.data
default:
return state
}
}
export const finalReducer = combineReducers({
name, age
})
其中state='initRedux' 、state=0 相当于我们在React组件内部初始化state
redux/store.js
一切操作还是基于Store 的。类似于中央集权。所以还要把Store建立出来
import {createStore, applyMiddleware} from 'redux'
import {finalReducer} from './reducers'
import thunk from 'redux-thunk'
//生成store对象
const store = createStore(finalReducer, applyMiddleware(thunk)); //内部会第一次调用reducer函数,得到初始state
export default store
因为reducer会更新Store中的状态(数据),所以需要引入reducer,并创建store
至此,流程图结束。不过2、3、4都是redux中负责接管React状态的功能。1是React负责展示的组件。两者并没啥关系。既然有了展示组件,接下来就要有容器组件,也就是能够将React与redux相关联的一个组件。
容器组件
containers/redux-app.js
import {connect} from 'react-redux'
import {nameCreator, ageCreator, nameAsync} from '../redux/actions'
import Person from '../component/person'
export default connect(
state => ({
name: state.name,
age: state.age
}),
{nameCreator, ageCreator, nameAsync}
)(Person)
这里用到react-redux
中的connect
,可以将React与redux关联起来。Person就是第一步写的组件名,state中关联了React中的属性。
添加store
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import {Provider} from 'react-redux'
import ReduxApp from './redux/containers/redux-app'
import store from './redux/redux/store'
ReactDOM.render((
//使用Provider 组件将APP主组件包裹住,这样内部组件都有Store种提供的属性
<Provider store={store}>
<ReduxApp/>
</Provider>
), document.getElementById('root'));
文件结构
redux
:集中管理状态(数据)component
:专注于React 展示组件部分containers
:集中处理React与redux交互的部分
此外,redux还可以处理一些异步请求,可以做到data和view的管理分离,增强了工程结构的可读性与可维护性。
深入理解Redux
Action与Reducer的详细理解
先回顾一下不使用Redux的情况下,React的Class该怎么写
import React, {Component} from 'react';
class Test extends Component {
constructor(props){
super(props);
state = {
nickname: ''
}
}
handlerFunc = () => {
const inputName = this.refs.inputValue.value;
this.setState({
nickname: inputName
})
}
render() {
const {nickname} = this.state;
return (
<div>
<label> {nickname} </label><br/>
<input ref="inputValue" /><br/>
<button onClick={this.handlerFunc}>confirm</button><br/>
</div>
);
}
}
主要流程:constructor
里初始化状态,然后渲染组件,onClick
里绑定点击事件,事件函数中setState()
一下,此时得到新的状态,并使虚拟DOM重新渲染,最终Label显示我们输入的值。
然后看看使用Redux后,组件是怎么写的:
import React, {Component} from 'react';
import PropTypes from 'prop-types'
class Test extends Component {
static propTypes = {
nickname:PropTypes.string.isRequired,
addName:PropTypes.func.isRequired
}
handlerFunc = () => {
const inputName = this.refs.inputValue.value;
this.props.addName(inputName);
}
render() {
const {nickname} = this.props;
return (
<div>
<label> {nickname} </label><br/>
<input ref="inputValue" /><br/>
<button onClick={this.handlerFunc}>confirm</button>
</div>
);
}
}
观察两种写法,发现:
(1)初始化状态没有了
(2)setState()
也移除了,然后通过属性结构赋值
总之,状态管理方面的东西都被移除。因为采用Redux框架,本身就是要让其接管状态的,所以,redux框架下的React代码就很好理解了。就是react组件里面是没有State相关的东西(当然,特殊情况下还是需要用的组件自己的状态管理的)。redux所要做的就是,传递动作(按钮点击)或者请求(请求数据),并根据自身属性向HTML中填入数据。这里用到了prop-types
这个库。来进行属性的校验。
之后可以这样理解,setState()
方法在Redux那边被拆分成两步,现在Action中,把获取到的值,拼装成一个Object,代码如下:
import { NAME } from "./action-type";
//包含所有的action creator
export const addName = (name) => ({type:NAME, data:name})
这里面的addName 就是React代码里按钮触发的方法中要执行的函数,name 就是我们或得到的输入框里的值。
接着redux帮我们做的就是用Store中提供的dispatch
方法,把这个动作传到reducer那去,然后来看看Reducer做了什么:
import {NAME} from './action-type'
const initialState = {
name: 'Joe.Ye'
}
const name = (state=initialState, action) => { //形参默认值
switch(action.type){
case NAME:
return action.data
default:
return state
}
}
Reducer可以类别理解为:既充当了setState()
的角色,也负责了初始化state的任务
所以,reducer在case到NAME这个动作之后,就把从action传来的值,做了一个相当于setState()的操作:
this.setState({
name: action.data
})
要是一开始没有触发任何动作,则Store会帮我们在程序运行之初执行一遍Reducer,使用初始值initialState
展示组件与容器组件
redux写的代码单拎出来与react代码是没啥联系的。写完上面的代码后,一运行就会报错,一大堆undefined。这个时候就需要一个connect来连接Redux和React。现在有这么一个库react-redux
,使用里面的connect
即可。如下:
import React from 'react'
import {connect} from 'react-redux'
import {addName} from '../redux/actions'
import Test from '../component/Test'
export default connect(
state => ({
nickname: state.name
}),
{addName}
)(Test)
(1)容器组件
这样就写好了一个组件,官方称之为容器组件,就是引入之前写好的React组件Test,再引入之前action和reducer里的动作、状态,进行一个封装,这样就把Test里事件函数与属性匹配到了一起。
其中nickname对应Test里面的nickname属性,state.name
就是reducer里定义的那个name。到时候Test组件里更新nickname的值,就是state.name
给赋上去的。属性状态匹配完之后要匹配动作,Test中点击事件中执行的addName,就匹配action里的addName。这样,点击按钮之后,数据就会通过action传到Reducer那去做类似于setState()这样的操作。
(2)展示组件
就是Test那个组件,里面一般不包含任何Redux相关的东西,纯净的React组件,称之为展示组件。可以理解为容器组件(封装组件)的一部分
异步
一个web应用,大多数都需要做请求操作的。而在React + Redux
这样的一个结构中,异步请求操作最合适的位置就是放到Redux中的action中。首先,展示组件,即Test组件,这个是view层。view层显然不适合掺杂异步请求的。然后reducer其实是实现的类似于setState()
的作用。所以在Redux中的Action中做异步请求是比较合适的。action其实也是个传接数据的载体。
既然选择在Redux中进行异步请求,就需要引入一个库react-thunk
,来帮助我们进行异步请求。首先进行配置:
import {createStore, applyMiddleware} from 'redux'
import {finalReducer} from './reducers'
import thunk from 'redux-thunk'
//生成store对象
const store = createStore(finalReducer, applyMiddleware(thunk)); //内部会第一次调用reducer函数,得到初始state
export default store
正常来说,一般我们是触发一个动作进行请求,得到数据后,再去setState。在Action中也是一样,如下:
export const nameAsync = (name) => {
return dispatch => {
setTimeout(() => {
dispatch(nameCreator(name))
}, 2000);
}
}
这里用setTimeout模拟请求过程。两秒后,数据请求到了,再把数据给Reducer进行状态替换。这里我们需要手动调用dispatch这个函数,向Reducer去分发动作。
版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/12/react-redux-basic-usage-process/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论