react中的状态管理-redux

  1. 英文文档: https://redux.js.org/
  2. 中文文档: http://www.redux.org.cn/
  3. Github: https://github.com/reactjs/redux

一、redux

1、redux是什么?

  1. redux是一个专门用于做状态管理的JS库;
  2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用;
  3. 作用: 集中式管理react应用中多个组件共享的状态。

2、什么时候需要使用redux?

  1. 某个组件的状态,需要让其他组件可以随时拿到(共享)。
  2. 一个组件需要改变另一个组件的状态(通信)。
  3. 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。

3、redux工作流程

在这里插入图片描述

4、redux的三个核心概念

4.1、action

  1. 动作的对象
  2. 包含2个属性
     type:标识属性, 值为字符串, 唯一, 必要属性
     data:数据属性, 值类型任意, 可选属性
  3. 例子:{ type: ‘ADD_STUDENT’,data:{name: ‘tom’,age:18} }

4.2、reducer

  1. 用于初始化状态、加工状态。
  2. 加工时,根据旧的state和action, 产生新的state的纯函数

4.3、store

  1. 将state、action、reducer联系在一起的对象
  2. 如何得到此对象?
    1. import {createStore} from ‘redux’
    2. import reducer from ‘./reducers’
    3. const store = createStore(reducer)
  3. 此对象的功能?
    1. getState(): 得到state
    2. dispatch(action): 分发action, 触发reducer调用, 产生新的state
    3. subscribe(listener): 注册监听, 当产生了新的state时, 自动调用

5、redux的核心API

5.1、createstore()

  作用:创建包含指定reducer的store对象

5.2、store对象

  1. 作用: redux库最核心的管理对象
  2. 它内部维护着:
    1. state
    2. reducer
  3. 核心方法:
    1. getState(),获取状态
    2. dispatch(action),派发行为
    3. subscribe(listener),监测redux的状态变化
  4. 具体编码:
    1. store.getState()
    2. store.dispatch({type:‘INCREMENT’, number})
    3. store.subscribe(render)

5.3、applyMiddleware()

  作用:应用上基于redux的中间件(插件库)

5.4、combineReducers()

  作用:合并多个reducer函数

6、异步action

  1. redux默认是不能进行异步处理的,
  2. 某些时候应用中需要在redux中执行异步任务(ajax, 定时器)
  3. 使用异步中间件:npm install --save redux-thunk

7、redux的应用(求和案例)

运行结果
在这里插入图片描述

文件目录结构
在这里插入图片描述

1、创建store,store.js

/*该文件专门用于暴露一个store对象,整个应用只有一个store对象*/

//1、引入createStore,专门用于创建redux中最为核心的store对象,applyMiddleware:执行中间件
import {createStore, applyMiddleware} from "redux";

//2、引入为count组件服务的reducer
import countReducer from "./count_reducer";

//3、引入redux-thunk,用于支持异步action
import thunk from "redux-thunk";

//4、暴露store
export default createStore(countReducer, applyMiddleware(thunk));

2、count_reducer

/*
* 1、初始化状态
* 2、加工状态
* 该文件用于创建一个为count组件服务的reducer,reducer的本质就是一个函数;
* reducer会接收到两个参数,分别为:之前的状态(preState),行为(action)
* */
import {INCREMENT,DECREMENT} from "./constant";

const initState = 7;

function countReducer(preState = initState, action) {
    console.log(preState, action);
    const {type, data} = action;
    switch (type) {
        case INCREMENT://return preState + data;
        case DECREMENT://return preState - data;
        default:
            return preState
    }
}

export default countReducer

3、count_action.js

/*该文件专门为count组件生成action对象*/
import {INCREMENT, DECREMENT} from "./constant";
import store from "./store";

//同步action,就是指action的值为object类型的一般对象
export const createIncrementAction = data => ({type: INCREMENT, data})
export const createDecrementAction = data => ({type: DECREMENT, data})
//所谓的异步action,就是指action的值为函数,需要使用一个中间插件,
// 异步action中一般都会调用同步action,异步action不是必须要用的
export const createIncrementAsyncAction = (data, time) => {
    //返回的是一个函数
    return () => {
        setTimeout(() => {
            store.dispatch(createIncrementAction(data))
        }, time)
    }
}

4、constant.js

/*
* 该模块是用于定义action对象中type类型的常量值,
* 便于管理的同时,当值程序员单词写错,降低单词拼写出错率*/
export const INCREMENT = "increment";
export const DECREMENT = "decrement";

5、count/index.js

import React, {Component} from 'react';
//1、引入store,用于获取redux中保存的状态
import store from "../../redux/store";
//2、引入actionCreator,专门用于创建对象
import {createDecrementAction, createIncrementAction, createIncrementAsyncAction} from "../../redux/count_action";

export default class Count extends Component {
    /*    componentDidMount() {
            //5、组件挂载完成后监测redux中的状态变化,只要变化就调用render渲染页面
            store.subscribe(() => {
                this.setState({})
            })
        }*/

    increment = () => {
        const {value} = this.selectNumber;
        // 4、通知redux加value(store.dispatch进行行为派发)
        store.dispatch(createIncrementAction(value * 1))

    }
    decrement = () => {
        const {value} = this.selectNumber;
        store.dispatch(createDecrementAction(value * 1))
    }
    //奇数再加
    incrementIfOdd = () => {
        const {value} = this.selectNumber;
        const count = store.getState()
        if (count % 2 !== 0) {
            store.dispatch(createIncrementAction(value * 1))
        }
    }
    //异步加
    incrementAsync = () => {
        const {value} = this.selectNumber;
        store.dispatch(createIncrementAsyncAction(value * 1, 500))
    }

    render() {
        return (
            <div>
                {/*3、store.getState()获取状态*/}
                <h1>当前求和为:{store.getState()}</h1>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                    <option value="4">4</option>
                </select><br/><br/>
                <button onClick={this.increment}>+</button>
                &nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
                &nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
            </div>
        )
    }
}

监测redux的状态变化也可以在index.js入口文件中完成

import App from './App';
import store from "./redux/store";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <React.StrictMode>
        <App/>
    </React.StrictMode>
);

//监测redux状态变化的api写在这里,就不用每个组件都需要调用store.subscribe
// 检测redux 状态 发生变化 整个app 都调用 render()  作用是不用重复写 componentDidMount
store.subscribe(() => {
    root.render(
        <React.StrictMode>
            <App/>
        </React.StrictMode>
    );
})

6、App.js
在App.js文件中引入组件count/index.js

在这里插入图片描述

二、react-redux

1、react-Redux将所有组件分成两大类

  1. UI组件
    1. 只负责 UI 的呈现,不带有任何业务逻辑
    2. 通过props接收数据(一般数据和函数)
    3. 不使用任何 Redux 的 API
    4. 一般保存在components文件夹下
  2. 容器组件
    1. 负责管理数据和业务逻辑,不负责UI的呈现
    2. 使用 Redux 的 API
    3. 一般保存在containers文件夹下

2、react-redux相关的API

2.1、Provider

  让所有组件都可以得到state数据

root.render(
    /*Provider让所有组件都可以得到state数据*/
    <Provider store={store}>
        <App/>
    </Provider>
);

2.2、connect

  用于包装 UI 组件生成容器组件

export const CountContainer = connect(
    mapStateToProps,
    mapDispatchToProps)(CountUI)

2.3、 mapStateToprops

  将外部的数据(即state对象)转换为UI组件的标签属性

const mapStateToProps = (state) => {
    //返回值是一个对象
    return {count: state}
}

2.4、mapDispatchToProps

  将分发action的函数转换为UI组件的标签属性

3、react-redux的应用(求和案例)

  1. 运行效果
    在这里插入图片描述

  2. 文件目录结构
    在这里插入图片描述

3.components/count/index.js

import React, {Component} from 'react';

export default class Count extends Component {
    increment = () => {
        const {value} = this.selectNumber;
        this.props.jia(value * 1)
    }
    decrement = () => {
        const {value} = this.selectNumber;
        this.props.jian(value * 1)
    }
    //奇数再加
    incrementIfOdd = () => {
        const {value} = this.selectNumber;
        if (this.props.count % 2 !== 0) {
            this.props.jia(value * 1)
        }
    }
    //异步加
    incrementAsync = () => {
        const {value} = this.selectNumber;
        this.props.jiaAsync(value * 1,500)
    }

    render() {
        console.log('UI组件', this.props);
        return (
            <div>
                <h1>当前求和为:{this.props.count}</h1>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                    <option value="4">4</option>
                </select><br/><br/>
                <button onClick={this.increment}>+</button>
                &nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
                &nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
            </div>
        )
    }
}

  1. containers/count/index.jsx
//这是一个容器组件

//1、引入connect用于连接UI组件与redux
import {connect} from "react-redux";

//2、引入Count的UI组件
import CountUI from "../../components/count";
//3、引入action
import {createIncrementAction, createDecrementAction, createIncrementAsyncAction} from "../../redux/count_action";

/*
* 1、mapStateToProps函数的返回是一个对象
* 2、返回的对象中key就作为状态传递给UI组件props的key,value就作为传递给UI组件props的值value
* 3、mapStateToProps用于传递状态*/
const mapStateToProps = (state) => {
    //返回值是一个对象
    return {count: state}
}

/*
* 1、mapDispatchToProps函数的返回是一个对象
* 2、返回的对象中key就作为状态传递给UI组件props的key,value就作为传递给UI组件props的值value
* 3、mapStateToProps用于传递操作状态的方法*/
const mapDispatchToProps = (dispatch) => {
    return {
        jia: (data) => {
            dispatch(createIncrementAction(data))
        },
        jian: (data) => {
            dispatch(createDecrementAction(data))
        },
        jiaAsync: (data, time) => {
            dispatch(createIncrementAsyncAction(data, time))
        }
    }
}

//4、connect()()创建并暴露Count的容器组件
//connect()第一次调用的时候必须传入两个参数,两个参数必须是函数
export const CountContainer = connect(
    mapStateToProps,
    mapDispatchToProps)(CountUI)

/*简写
 1. */
/*export const CountContainer = connect(
    state => ({count: state}),
 /!*   //mapDispatchToProps的一般写法
    dispatch => ({
        jia: data => dispatch(createIncrementAction(data)),
        jian: data => dispatch(createDecrementAction(data)),
        jiaAsync: (data, time) => dispatch(createIncrementAsyncAction(data, time))
    })*!/
    //mapDispatchToProps的简写
    {
        //reaact-redux会自动进行派发
        jia:createIncrementAction,
        jian:createDecrementAction,
        jiaAsync:createIncrementAsyncAction
    }
    )(CountUI)*/
  1. store.js
/*该文件专门用于暴露一个store对象,整个应用只有一个store对象*/

//1、引入createStore,专门用于创建redux中最为核心的store对象,applyMiddleware:执行中间件
import {createStore, applyMiddleware} from "redux";

//2、引入为count组件服务的reducer
import countReducer from "./count_reducer";

//3、引入redux-thunk,用于支持异步action
import thunk from "redux-thunk";

//4、暴露store
export default createStore(countReducer, applyMiddleware(thunk));
  1. count_reducer
/*
* 1、初始化状态
* 2、加工状态
* 该文件用于创建一个为count组件服务的reducer,reducer的本质就是一个函数;
* reducer会接收到两个参数,分别为:之前的状态(preState),行为(action)
* */
import {INCREMENT,DECREMENT} from "./constant";

const initState = 7;

function countReducer(preState = initState, action) {
    console.log(preState, action);
    const {type, data} = action;
    switch (type) {
        case INCREMENT://return preState + data;
        case DECREMENT://return preState - data;
        default:
            return preState
    }
}

export default countReducer
  1. count_action.js
/*该文件专门为count组件生成action对象*/
import {INCREMENT, DECREMENT} from "./constant";
import store from "./store";

//同步action,就是指action的值为object类型的一般对象
export const createIncrementAction = data => ({type: INCREMENT, data})
export const createDecrementAction = data => ({type: DECREMENT, data})
//所谓的异步action,就是指action的值为函数,需要使用一个中间插件,
// 异步action中一般都会调用同步action,异步action不是必须要用的
export const createIncrementAsyncAction = (data, time) => {
    //返回的是一个函数
    return () => {
        setTimeout(() => {
            store.dispatch(createIncrementAction(data))
        }, time)
    }
}

  1. constant.js
/*
* 该模块是用于定义action对象中type类型的常量值,
* 便于管理的同时,当值程序员单词写错,降低单词拼写出错率*/
export const INCREMENT = "increment";
export const DECREMENT = "decrement";
  1. App.js
//1、引入容器里面的CountContainer组件
import {CountContainer} from "./containers/count";
import './App.css';

function App() {
    return (
        <div className="App">
            <CountContainer/>
        </div>
    );
}

export default App;

  1. index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import store from "./redux/store";
import {Provider} from "react-redux"
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    /*Provider让所有组件都可以得到state数据*/
    <Provider store={store}>
        <App/>
    </Provider>
);

将UI组件和容器组件结合到一起

import {connect} from "react-redux";
import React, {Component} from 'react';
import {createIncrementAction, createDecrementAction, createIncrementAsyncAction} from "../../redux/count_action";

//定义UI组件
class Count extends Component {
    increment = () => {
        const {value} = this.selectNumber;
        this.props.jia(value * 1)
    }
    decrement = () => {
        const {value} = this.selectNumber;
        this.props.jian(value * 1)
    }
    //奇数再加
    incrementIfOdd = () => {
        const {value} = this.selectNumber;
        if (this.props.count % 2 !== 0) {
            this.props.jia(value * 1)
        }
    }
    //异步加
    incrementAsync = () => {
        const {value} = this.selectNumber;
        this.props.jiaAsync(value * 1, 500)
    }

    render() {
        console.log('UI组件', this.props);
        return (
            <div>
                <h1>当前求和为:{this.props.count}</h1>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                    <option value="4">4</option>
                </select><br/><br/>
                <button onClick={this.increment}>+</button>
                &nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
                &nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
            </div>
        )
    }
}

export const CountContainer = connect(
    state => ({count: state}),
    {
        jia: createIncrementAction,
        jian: createDecrementAction,
        jiaAsync: createIncrementAsyncAction
    }
)(Count)

4、react-redux数据共享案例

效果:

在这里插入图片描述

文件目录:

在这里插入图片描述

containers/count/index.jsx

import {connect} from "react-redux";
import React, {Component} from 'react';
import {createIncrementAction, createDecrementAction, createIncrementAsyncAction} from "../../redux/actions/count";

class Count extends Component {
    increment = () => {
        const {value} = this.selectNumber;
        this.props.increment(value * 1)
    }
    decrement = () => {
        const {value} = this.selectNumber;
        this.props.decrement(value * 1)
    }
    incrementIfOdd = () => {
        const {value} = this.selectNumber;
        if (this.props.count % 2 !== 0) {
            this.props.increment(value * 1)
        }
    }
    incrementAsync = () => {
        const {value} = this.selectNumber;
        this.props.incrementAsync(value * 1, 500)
    }

    render() {
        console.log('UI组件', this.props);
        return (
            <div>
                <h1>我是count组件</h1>
                <h1>当前求和为:{this.props.count}</h1>
                <h1>下方组件总人数为:{this.props.personTotal}</h1>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                    <option value="4">4</option>
                </select><br/><br/>
                <button onClick={this.increment}>+</button>
                &nbsp;
                <button onClick={this.decrement}>-</button>
                &nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
                &nbsp;
                <button onClick={this.incrementAsync}>异步加</button>
            </div>
        )
    }
}

export const CountContainer = connect(
    state => ({
        count: state.countReducer,
        personTotal: state.personReducer.length
    }),
    {
        increment: createIncrementAction,
        decrement: createDecrementAction,
        incrementAsync: createIncrementAsyncAction
    }
)(Count)

containers/person/index.jsx

import React, {Component} from 'react';
import {nanoid} from "nanoid"
import {connect} from "react-redux";
import {createAddPersonAction} from "../../redux/actions/person"


class Person extends Component {
    addPerson = () => {
        const name = this.nameNode.value;
        const age = this.ageNode.value;

        const personObj = {id: nanoid(), name, age};
        console.log(personObj);
        this.props.addOnePerson(personObj);
        this.nameNode.value = "";
        this.ageNode.value = "";
    }

    render() {
        return (
            <div>
                <h1>我是person组件</h1>
                <h1>上方组件的和为:{this.props.sum}</h1>
                <input ref={c => this.nameNode = c} type="text" placeholder="输入名字"/>
                <input ref={c => this.ageNode = c} type="text" placeholder="输入年龄"/>
                <button onClick={this.addPerson}>添加</button>
                <ul>
                    {this.props.person.map((item) => {
                        return (<li key={item.id}>名字:{item.name}----年龄:{item.age}</li>)
                    })}
                </ul>
            </div>
        )
    }
}

export const PersonContainer = connect(
    state => ({
        person: state.personReducer,
        sum: state.countReducer
    }),//映射状态
    {addOnePerson: createAddPersonAction}
)(Person)

redux/actions/count.js

import {INCREMENT, DECREMENT} from "../constant";
import store from "../store";

export const createIncrementAction = data => ({type: INCREMENT, data})
export const createDecrementAction = data => ({type: DECREMENT, data})

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

redux/actions/person.js

import {ADD_PERSON} from "../constant";

//创建增加一个人的动作对象
export const createAddPersonAction=personObj=>({type:ADD_PERSON,data:personObj})

redux/reducers/count.js

import {INCREMENT,DECREMENT} from "../constant";

function countReducer(preState = 7, action) {
    console.log(preState, action);
    const {type, data} = action;
    switch (type) {
        case INCREMENT://return preState + data;
        case DECREMENT://return preState - data;
        default:
            return preState
    }
}

export default countReducer

redux/reducers/person.js

import {ADD_PERSON} from "../constant"

export default function personReducer(prevState = [{id: "001", name: "tom", age: 12}], action) {
    const {type, data} = action;
    switch (type) {
        case ADD_PERSON:
            return [data, ...prevState];
        default:
            return [...prevState]
    }
}

redux/reducers/index.js:用于汇总多个reducer

//该文件用于汇总所有reducer
//引入combineReducers,用于汇总对个reducer
import {combineReducers} from "redux";

import countReducer from "./count";
import personReducer from "./person";
//合并reducer,保存的总状态是一个对象
const allReducer = combineReducers({
    countReducer, personReducer
})
export default allReducer;

redux/costant.js

export const INCREMENT = "increment";
export const DECREMENT = "decrement";
export const ADD_PERSON="addPerson"

redux/store.js

/*该文件专门用于暴露一个store对象,整个应用只有一个store对象*/

//1、引入createStore,专门用于创建redux中最为核心的store对象,applyMiddleware:执行中间件
import {createStore, applyMiddleware} from "redux";
//可以在浏览器中使用插件观察redux的状态变化
import {composeWithDevTools} from "redux-devtools-extension";
//2、引入汇总后的reducer
import allReducer from "./reducers";
//3、引入redux-thunk,用于支持异步action
import thunk from "redux-thunk";
//4、暴露store
export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)));


App.js

import {CountContainer} from "./containers/count";
import {PersonContainer} from "./containers/person";
import './App.css';

function App() {
    return (
        <div className="App">
            <CountContainer/>
            <hr/>
            <PersonContainer/>
        </div>
    );
}
export default App;

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import store from "./redux/store";
import {Provider} from "react-redux"
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    /*Provider包裹App,Provider让所有组件都可以得到state数据*/
    <Provider store={store}>
        <App/>
    </Provider>
);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值