React-Redux 使用

目前使用版本

"redux": "^4.0.5"
"react-redux": "^7.2.0",

建议先了解 Redux 的使用!Redux使用(纯Redux使用)

React-Redux 是 Redux 和 React 的绑定库!

为什么要使用这个 React-Redux 库:

在之前的文章Redux使用(纯Redux使用)里面使用的是 store.subscribe()的方式进行监听了store 的里面的state的变化!虽说这样做也可以,但是会有其他的性能问题,比如:渲染页面重复的问题,你可能通过生命周期函数shouldComponentUpdate可以处理,但是这样太繁琐,特别是组件越来越复杂之后!
React-Redux 这个库在官方说明中是有性能优化的!特别是提供了一种connect()方式来进行包装组件,这种方式就是我们在做 容器化组件展示组件 进行 分离的时候的 容器化组件

  • 容器组件:处理 props 或者 state
  • 展示组件:只负责展示界面

比如说:你把网路请求放到 容器组件 中,然后在 容器组件 里面把数据组装好,然后传递给 展示组件 直接渲染页面展示!
可以看看我之前的文章:React高阶组件

核心API

  • <Provider store={store}>...</Provider>:这个组件是重点,要在你的根目录下的index.js文件把根组件进行包裹
import TodoList from './list/TodoList';
import { Provider } from 'react-redux'
import store from './redux/store/index'
========================= 省略一大波代码 ======

// 重点这里
<Provider store={store}>
      <TodoList />
</Provider>, 
  • connect(....) :连接函数,把组件和redux进行关联!这个是重点函数,这里可以把 statedispatch 传递进去!

connect(....) 有几个参数很关键(重点关注两个):

  • mapStateToProps(state, [ownProps]):如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps 函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并!

这个函数,就是监听 Redux store 的数据变化的!!

/**
 * 把 store 里面 state 状态,附加到 props 上
 * 这里的参数两个参数,connect()自动赋值
 * @param {*} state store的全局状态,connect()自动赋值
 * @param {*} ownProps 组件本身自己带的props, connect()自动赋值
 */
const mapStateToProps = (state, ownProps) => {

    /**
     * 这里可以只拿取你需要的 state 里面的数据,这样可以节省性能开销
     * 比如我这里只要 state 里面的 list
     * 然后组件里面可以通过 props 获取 list 的值
     */

    return { list: state.list }
}
  • mapDispatchToProps(dispatch, [ownProps]):如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;每个方法将返回一个新的函数,函数中dispatch方法会将 action creator 的返回值作为参数执行。这些属性会被合并到组件的 props 中。

(1):这个函数,就是用来执行dispatch的,就是说可以把这个组件中所有的dispatch操作从组件里面都提取出来统一管理!
(2):如果不定义 mapDispatchToProps ,那么默认把所有的dispatch都传递到组件中,那么你可以在组件中直接操作dispatch

/**
 * 把里面定义的函数附加到 props 上
 * 组件可以通过 props.onTodoClick(id) 进行调用
 */
const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    /**
     * 可以定义多个函数
     * 组件可以通过 props.onTodoClick(id) 进行调用
     **/
    onTodoClick: id => {
      dispatch(toggleTodo(id))
    }
  }
}

开始使用 React-Redux

安装依赖

// redux 相关
yarn add redux react-redux redux-devtools-extension --save

这里安装了三个库:reduxreact-reduxredux-devtools-extension

  • redux 是 Redux 的原生核心库
  • react-redux 是 React 和 Redux 的绑定库(有额外的性能优化)
  • redux-devtools-extension 是一个开发组工具库可以在浏览器上看到 store 里面的 state 的变化,方便调试!

这里例子使用了 Antd 组件库!

先定义 action

// redux/action/list/index.js

/**
 * Action 类型
 *      事件类型
 */
export const type = {
    ADD_LIST: 'ADD_LIST'
}

/**
 * 添加 List
 * @description 也就是获取添加 List 操作类型, 以及携带的数据
 * @param {*} data 携带的数据
 */
export function addList(data) {
    return {
        type: type.ADD_LIST,
        data
    }
}

定义 reducer

// redux/reducer/list/index.js

import { type } from '../../action/list'

 // 数据仓库
 const initialState = {
    data: [
        "这是第一行",
        "这是第二行",
        "这是第三行"
    ],
 }

 /**
  *  list 数据处理
  * @param {*} state 
  * @param {*} action 
  */
export default function list(state = initialState, action) {
    switch (action.type) {
        case type.ADD_LIST:

            /**
             * 处理完一些里操作后,返回一个新的 state
             */

            // 把新的值,添加进 state
            const newData = state.data.concat(action.data)

            return {
                ...state,
                data: newData
            }
        default:
            return { ...state }
    }
}

集中我们定义了一系列的 reducer 的函数

// redux/reducer/index.js

/**
 * Reducer 数据处理
 */
import { combineReducers } from 'redux'
import list from './list'

/**
 * 把多个 reducer 进行合并成一个
 */
export default combineReducers({

    /**
     * 写入一系列的 reducer
     */

    list
})

创建 store

/**
 * 创建数据源  store
 */
import { createStore } from 'redux'
import reducer from '../reducer'
import { composeWithDevTools }  from 'redux-devtools-extension'

// 创建 store
// reducer 一定要是一个函数
// composeWithDevTools() 一定要写成这样,不能写成 composeWithDevTools
const store = createStore(reducer, composeWithDevTools())
export default store

创建 TodoList.js

import React, { Component } from 'react'
import { Input, Button, List } from 'antd';
import { connect } from 'react-redux'
import { addList } from '../redux/action/list'

/**
 * TodoList 一个列表
 */
class TodoList extends Component {

    constructor(props) {
        super(props)
        
        this.inpVal = React.createRef();

        this.state = {
            // data: [
            //     "这是第一行",
            //     "这是第二行",
            //     "这是第三行"
            // ],
            data: []
        }

    }

    componentDidMount() {
        this.setState({ data: this.props.list.data || [] })
    }

    componentDidUpdate(prevProps) {
        // 典型用法(不要忘记比较 props)
        if (this.props.list.data !== prevProps.list.data) {
            this.setState({ data: this.props.list.data || [] })
        }
    }

    addData() {
        const { dispatch } = this.props
        const inputValue = this.inpVal.current.state.value
        console.log('当前值:', inputValue)
        if (inputValue === undefined) {
            return
        }
        // this.setState({ data: this.state.data.concat(inputValue) })
        // 更新store
        dispatch(addList(inputValue))
        this.inpVal.current.state.value = undefined
    }

    render() {
        // console.log('this.props: ', this.props)
        return (
            <>
                <div style={{ margin: '10px' }}>
                    <div>
                        <Input ref={ this.inpVal } placeholder="请输入内容" style={{ width: '200px' }}/>
                        <Button type="primary" style={{ marginLeft: '10px' }} onClick={this.addData.bind(this)}>确认</Button>
                        <List
                            style={{ marginTop: '10px', width: '200px' }}
                            bordered
                            dataSource={this.state.data}
                            renderItem={(item, index) => (
                                <List.Item key={index}>
                                    {item}
                                </List.Item>
                            )}
                        />
                    </div>
                </div>
            </>
        )
    }
}

/**
 * 把 store 里面 state 状态,附加到 props 上
 * @param {*} state store的全局状态
 * @param {*} ownProps 组件本身自己带的props
 */
const mapStateToProps = (state, ownProps) => {

    /**
     * 这里可以只拿取你需要的 state 里面的数据,这样可以节省性能开销
     * 比如我这里只要 state 里面的 list
     * 然后组件里面可以通过 props 获取 list 的值
     */

    return { list: state.list }
}

/**
 * connect 是一个容器
 * 把 原有的组件进行装饰一下(装饰者模式)
 */
export default connect(mapStateToProps)(TodoList)


修改根目录下 index.js 文件

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import TodoList from './list/TodoList';
import { Provider } from 'react-redux'
import store from './redux/store/index'
import * as serviceWorker from './serviceWorker';

ReactDOM.render(
    <Provider store={store}>
        <TodoList />
    </Provider>, 
    document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

结果:

结果截图

题外话:

结合实际项目开发的时候,redux 肯定是分为多个子模块的。可以把 actionreducer 拆分成多个!那么 reducer 拆分后,在定义一个统一的 reducer 文件进行合并!通过 redux 提供的 combineReducers 函数可以把多个子reducer进行合并!

示例图

代码:https://github.com/weiximei/redux-demo
ps: 平常码云用的最多!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容