React+Redux基本使用流程

Redux简介

Redux是React生态中重要的组成部分。React提出将展示组件与容器组件分离的思想,降低了React与Redux之间的耦合度。

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全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
React+Redux基本使用流程
Redux简介 Redux是React生态中重要的组成部分。React提出将展示组件与容器组件分离的思想,降低了React与Redux之间的耦合度。 Store:整个应用的数据存储中……
<<上一篇
下一篇>>
文章目录
关闭
目 录