redux使用详解

        一个状态管理的JS库,用于数据的共享。结构如下:

安装:

yarn add redux

一、简化版的redux

1、src/reducer/store.js

//引入createStore
import {createStore} from 'redux'
//引入服务的reducer
import reducer from "./reducer";

export default createStore(reducer)

2、src/reducer/reducer.js

const defaultState = {
    count: 0
}

const reducer = (preState = defaultState, action) => {
    const {type, data} = action
    switch (type) {
        case 'add':
            return {
                count: preState.count + data
            }
        case 'reduce':
            return {
                count: preState.count - data
            }
        default:
            return defaultState
    }
}

export default reducer

3、src/reducer/App.js

import React, {Component} from 'react';
import {Button} from 'antd'
import 'antd/dist/antd.css'
//用于获取状态
import store from "./reducer/store";

export default class App extends Component {

    componentDidMount() {
        //reducer不会触发页面变化,需要state来触发
        store.subscribe(() =>{
            this.setState(store.getState())
        })
    }

    render() {
        //获取reducer数据
        const {count} = store.getState()
        return (
            <div>
                <Button type='primary' onClick={this.reduce}>-</Button>
                <span>{count}</span>
                <Button type='primary' onClick={this.add}>+</Button>
            </div>
        );
    }

    reduce = () => {
        //通知reducer页面数据变化了
        store.dispatch({
            type: 'reduce',
            data: 1
        })
    }

    add = () => {
        //通知reducer页面数据变化了
        store.dispatch({
            type: 'add',
            data: 1
        })
    }
}

二、完整版的redux

1、为了防止type被多个js引用,不小心写错,引发报错,需要将type设置为常量,创建src/reducer/typeCreators.js文件:

export const ADD = 'add_type'
export const REDUCE = 'reduce_type'

2、创建src/reducer/actionCreators.js文件。统一抽取action:

import {ADD, REDUCE} from "./typeCreators";

export const addAction = (data) => (
    {
        type: ADD,
        data
    }
)

export const reduceAction = (data) => (
    {
        type: REDUCE,
        data
    }
)

三、redux 的异步action

        如果action是一个对象,则是同步action;如果action是一个function,这是一个异步action,直接使用action返回函数redux不允许,直接报错,redux认识返回一个对象的action,如下方式是不被允许的:

import store from "./store";

/**
 * 异步加的action:返回的是函数,直接使用这种方式会报错
 */
export const addActionAsync = (data, time) => {
    return ()=>{
        setTimeout(()=>{
            store.dispatch(addAction(data))
        },time)
    }
}

        这是需要使用一个中间件来实现异步的的功能,即:redux-thunk。

四、redux-thunk实现异步action

yarn add redux-thunk

1、src/reducer/store.js

import {createStore,applyMiddleware} from 'redux'
import thunk from "redux-thunk";
import reducer from "./reducer";

/**
 * 该文件用于暴露一个store对象
 */
export default createStore(reducer, applyMiddleware(thunk))

        这时候直接运行addActionAsync就不会报错了。

        addActionAsync本身执行在store中,返回值默认有一个参数dispatch,不需要额外的引入,可以简化为

/**
 * 简化版
 */
export const addActionAsync = (data, time) => {
    return (dispatch) => {
        setTimeout(() => {
            //异步任务一般在函数内都会调用同步action
            dispatch(addAction(data))
        }, time)
    }
}

        异步action不是一个必须的需求,异步任务可放在action中执行,也可以放在组件中执行。

五、react-redux

yarn add react-redux

        redux是非官方的,使用其他也没啥问题,由于比较好用,官方又集成了redux,推出react-redux,用法更为灵活。如下模型图:

六、react-redux基本使用

  • UI组件:一般放在components文件夹下
  • 容器组件:一般放在container文件夹下

1、src/components/count/index.jsx:

export default class CountUI extends Component {

    render() {
        console.log('UI组件接收到的props:', this.props)
        return (
            <div>
                <Button type='primary' onClick={this.reduce}>-</Button>
                <span>{this.props.count}</span>
                <Button type='primary' onClick={this.add}>同步+1</Button>
                <Button type='primary' onClick={this.addAsync}>异步+2</Button>
            </div>
        );
    }

    reduce = () => {
        this.props.reduce(10)
    }

    add = () => {
        this.props.add(5)
    }

    addAsync = () => {
        this.props.addActionAsync(2, 500)
    }
}

2、src/containers/count/index.jsx:

//引入UI组件
import CountUI from "../../components/count";
//引入connect用于连接UI组件和redux
import {connect} from "react-redux";
//引入action
import {
    addAction,
    reduceAction,
    addActionAsync
} from "../../reducer/actionCreators";

//----->状态
function mapStateToProps(state) {
    return {
        count: state.count
    }
}

//----->操作状态的方法
function mapDispatchToProps(dispatch) {
    return {
        add: number => dispatch(addAction(number)),
        reduce: number => dispatch(reduceAction(number)),
        addActionAsync: (number, time) => dispatch(addActionAsync(number, time))
    }
}

//使用connect创建并暴露一个count的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI)
  • mapStateToProps

1、mapStateToProps函数返回一个对象

2、返回的key作为传递给UI组件的props的key,value作为UI组件props的value

3、mapStateToProps用于传递状态

  • mapDispatchToProps

mapDispatchToProps函数返回一个对象

返回的key作为传递给UI组件的props的key,value作为UI组件props的value

mapDispatchToProps用于传递状态的方法

七、react-redux优化使用

1、mapDispatchToProps简写

const mapStateToProps = state => ({count: state.count})

//mapDispatchToProps一般写法
const mapDispatchToProps = dispatch => ({
    add: number => dispatch(addAction(number)),
    reduce: number => dispatch(reduceAction(number)),
    addActionAsync: (number, time) => dispatch(addActionAsync(number, time))
});

//mapDispatchToProps精简写法,react-redux可以自动调用dispatch完成分发
const mapDispatchToProps = {
    add: addAction,
    reduce: reduceAction,
    addActionAsync: addActionAsync
};

//参数1:映射状态
//参数2:映射状态的操作方法
export default connect(mapStateToProps, mapDispatchToProps)(CountUI)

        即:mapDispatchToProps既可以当做函数,也可以当做对象。

2、无需自己给组件设置store,只需要给外壳设置

import store from "./reducer/store";
import {Provider} from "react-redux";

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

3、容器组件和UI组件合而为一,将connect写在UI组件的外面

src/containers/count/index.jsx:

import React, {Component} from 'react';
import {connect} from "react-redux";
import {Button} from 'antd'
import 'antd/dist/antd.css'
import {
    addAction,
    reduceAction,
    addActionAsync
} from "../../reducer/actionCreators";

class Count extends Component {

    render() {
        return (
            <div>
                <Button type='primary' onClick={this.reduce}>-</Button>
                <span>{this.props.count}</span>
                <Button type='primary' onClick={this.add}>同步+1</Button>
                <Button type='primary' onClick={this.addAsync}>异步+2</Button>
            </div>
        );
    }

    reduce = () => {
        this.props.reduce(10)
    }

    add = () => {
        this.props.add(5)
    }

    addAsync = () => {
        this.props.addActionAsync(2, 500)
    }
}

const mapStateToProps = state => ({count: state.count})

const mapDispatchToProps = {
    add: addAction,
    reduce: reduceAction,
    addActionAsync: addActionAsync
};

export default connect(mapStateToProps, mapDispatchToProps)(Count)

4、使用react-redux之后不需要自己使用store.subscribe检查redux中状态改变了,容器组件自动完成。

八、combineReducers

        当有多个reducer时候,需要将reducer合并成一个,否则createStore只接受一个reducer,其他的reducer就没有引入,这时候用到combineReducers:

import {createStore, applyMiddleware, combineReducers} from 'redux'
import thunk from "redux-thunk";
import countReducer from "./count/reducer";
import personReducer from "./person/reducer";

//所有的reducer保存为一个reducer
const allReducer = combineReducers({
    c: countReducer, //count可以根据需求任意命名,比如:c,haha,shu等等
    p: personReducer
})

export default createStore(allReducer, applyMiddleware(thunk))

        在容器组件中使用的时候:

const mapStateToProps = state => {
    //注意这块有个c,需要和combineReducers中的对应
    return {count: state.c.count}
}

        还可以简化,引入reducer.js:

import countReducer from "../reducers/count";
import personReducer from "../reducers/person";
import {combineReducers} from "redux";

//所有的reducer保存为一个reducer
export default combineReducers({
    c: countReducer, //count可以根据需求任意命名,比如:c,haha,shu等等
    p: personReducer
})

        在store.js中:

import {createStore, applyMiddleware} from 'redux'
import thunk from "redux-thunk";
import {composeWithDevTools} from 'redux-devtools-extension';
import allReducer from "./reducers";

export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))

九、react-redux项目结构分类

有时候containers也可以为components包,没有强制要求。 

redux其他使用案例参考:

react-redux实现todolist功能_一杯清泉的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值