redux及react-redux基础使用

redux应该是react开发中最为大家熟知的一个进行统一状态管理的库了。不过值得一提的是,redux并非必须配合react去使用,它是可以单独使用去进行状态管理的,或者配合vue等框架去使用。

基本的redux

整个流程可以理解为在组件中通过dispatch一个携带要修改的数据的类型和参数的action交给store,然后store交给reducer去执行。reducer将数据处理后再更新store中的数据,组件就可以通过getState()获取共享的状态值。
image

包括一下几个主要部分的定义:

1、reducer

通常可以被命名为xxx_reducer.js。文件export一个方法,通过该方法对state进行修改,方法接收两个参数:之前的state值和action对象。action对象包含两部分,一是操作类型,二是操作携带的参数。通常会给第一个值指定一个默认值,作为该state的初始值。

const countRecucer = (preState = 0, action)=>{
    const { type, data } = action;
    switch(type){
        case "increment":
            return preState + data;
        default:
            return preState;
    }
}

这里要注意,reducer要是一个纯函数。所谓纯函数是这样的一类函数:只要是同样的输入,必定会得到同样的输出,有以下几个特征:

  1. 不得改写参数。
  2. 不会产生副作用,如网络请求、输入和输出设备等。
  3. 不能调用Date.now()或者Math.random()等不纯的方法。

这里如果违背reducer是一个纯函数的要求,输出的对象仍是输入的preState,比如往数组中push值,往对象中增加新key等等,redux是无法更新视图的。

另外值得一提的是,在组件中调用store.getState()获取值的时候,redux会自动帮我们调reducer去做初始化,会发送一个如下的action对象进行初始化。

{type: '@@redux/INIT0.g.h.f.l'}

2、store.js

store是整个redux的核心,组件中正是使用这个文件export出去的store对象。这里需要借助redux提供的createStore()方法去创建该对象。

import { createStore } from 'redux'
import countReducer from './count_reducer'

export default createStore(countReducer)

3、在组件中使用

剩下的就是在组件中使用了,通过以下代码就能在组件中dispatch一个action,从而修改共享的状态值。

store.dispatch({type:'xxx',data:xxx})

组件中也可以再通过store.getState()获取存在redux中的共享状态值。

补充

以上就是一个最简单的redux使用的流程了,但也需要再注意以下几个问题:

状态修改,组件更新

仅仅修改state的值是不能再页面上体现出来的。redux只是帮我们做了数据的存取修改,并没有帮我们调用render。

所以需要我们手动触发render,可以在钩子函数中做监听,如下:

componentDidMount(){
    store.subscribe(()=>{
        this.setState();
    })
}

或者在全局的index.js中去刷新所有组件:

store.subscribe(()=>{
    ReactDOM.render(<App />,document.getElementById('root'))
})

ActionCreator

可以通过一个方法,返回一个对象的方式去创建action,而不是直接写action对象,这样也方便接下来实现异步action。

export const createIncrementAction = (data)=>{
    return {type:'increment', data}
}

异步action

通过方法创建action,不返回一个对象而是返回一个方法,就能实现异步action,在action中执行一些异步操作。

export const createIncrementAsyncAction = (data, time)=>{
    return (dispatch)=>{
        setTimeout(()=>{
            dispatch(createIncrementAction(data))
        }, time)
    }
}

注意,如果使用方法作为返回值的方式去创建action,需要在store.js中用一个中间件配合:

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import countReducer from './count_reducer'

export default createStore(countReducer, applyMiddleware(thunk))

以上这两种借用方法创建action的方式,可以在组件中这样使用:

store.dispatch(createIncrementAction(data))
store.dispatch(createIncrementAsyncAction(data,1000))

常量 constant.js

因为在reducer和action中都要写同一个action的type,故可以单独提一个文件,维护常量供二者使用,防止修改手误,一般放在constant.js中。

export const INCREMENT = Symbol();

多组状态

我们可能会遇到不同模块的组件共用不同的对象,所以就有了多组组件分别保存自己的state到redux中这种需求。

首选需要明确的是,全局只有一个redux,一个store,故所有的内容都应白存在这一个里。之前只有一个组件,故只有一个reducer,所以创建store第一个参数就直接是这个reducer。但是如果有多个reducer,store.js就需要如下的操作了:

import { createStore, applyMiddleware, combineReducer } from 'redux'
import thunk from 'redux-thunk'

import countReducer from './count_reducer'
import personReducer from './person_reducer'

const allReducer = combineReducer({
    count: count_reducer,
    person: person_reducer
})
export default createStore(allReducer, applyMiddleware(thunk))

注意,这个时候存储在redux中的store就变成allReducer中这个对象,也就是:

{
    count: countReducer中返回的值,
    person: personReducer中返回的值
}

在组件中取值,store.getState()取到的也是这个值。

react-redux

这是一个react提供的组件,配合redux实现状态的管理,能够让我们无需手动触发render(),它会在state改变之后,自动帮我们更新视图。

react-redux需要我们罢组件分成容器组件和UI组件,它们是父子关系,容器组件才真正跟redux打交道,里面可以随意使用redux的api,但UI组件不能够使用任何redux的api。

容器组件会传给UI组件:1、redux中所保存的状态,2、用于操作状态的方法。以上两者都是通过props传递的。原理图如下:
image

容器组件

最关键的部分就是容器组件的创建,容器组件的创建使用的是react-redux提供的connect()()这个方法。这是一个高阶方法,第二个括号中的参数就是UI组件,假设我们有UI组件<Count />,则使用方式是:

import { connect } from 'react-redux'

export default connect()(Count)

而第一个括号中,传递的是两个方法。分别是将state映射成props的方法和将dispatch映射成props的方法。通过这两个方法,UI组件就能使用redux中存的状态和对状态操作的方法了。

import {connect} from 'react-redux'
import {crateIncrementAction, crateDecrementAction } from '../redux/count_action'

const mapStateToProps = (state)=>{
    return {
        count: state
    }
}
const mapDispatchToProps = (dispatch)=>{
    return {
        increment: (value)=>dispatch(createIncrementAction(value)),
        decrement: (value)=>dispatch(createDecrementAction(value))
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Count)

如上,创建好容器组件后,在UI组件中只需要通过使用this.props就可以使用count这个状态和increment、decrement这两个操作状态的方法去跟redux进行交互了。

而容器组件的使用,可以在使用的地方直接调用,同时传入state就行,假设我们有容器组件Count,则:

import store from '../redux/store'

<Count store={store} />

优化mapDispatchToProps

值得一提的是,mapDispatchToProps这个方法是可以进行优化的。它可以不使用一个方法,转而使用一个对象,有一个映射到生成action的对象就行了,以上提到的mapDispatchToProps可以优化成如下:

import {crateIncrementAction, crateDecrementAction } from '../redux/count_action'

const mapDispatchToProps = {
    increment: createIncrementAction,
    decrement: createDecrementAction
}

Provider

在使用容器组件时,需要传入store。实际上如果每个容器组件都需要传一遍就会比较麻烦,我们可以使用Provider进行优化:

import Count from './container/Count'
import { Provider } from 'react-redux'

export default class App extends Component {
  render() {
    return (
      <div id="app">
        <Provider store={store}>
          <Count />
        </ Provider>
      </div>
    );
  }
}

这样,在Provider中的每一个容器组件,都不需要再传入store了。

Redux DevTools

可以使用chrome的一个插件来帮助我们在开发中监控redux中的状态。

  1. 首先需要安装chrome的插件Redux DevTools
  2. 在项目中npm install redux-devtools-extension
  3. 在store.js中做如下修改
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import allReducer from './reducers'
import { composeWithDevTools } from 'redux-devtools-extension'

export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))
posted @ 2022-03-04 17:57  这个少年有点热丶  阅读(236)  评论(0编辑  收藏  举报