redux与react-redux学习笔记

一、redux

1、安装 yarn add redux

2、工作原理图解析

redux与react-redux学习笔记_第1张图片

流程步骤:

React Components(客人): react组件想要做什么事情,比如做加法运算

Action Creators(客人选择做菜的方式和选什么菜): 将菜单写好发送给饭店老板(store),action有两个参数 store.dispatch({ type: ‘用哪种方式做’, data: ‘做什么菜’ })

import store from '../../redux/store';
import { createIncrementAction, createDelcrementAction } from '../../redux/count_action';

 inCrement = () => {
        const { value } = this.selectState; // 这是要传入计算的值
        // createIncrementAction这个是从count_action.js中封装好的action方法
     	store.dispatch(createIncrementAction(value)); 
    };

Store(饭店老板): 将菜单发给厨师,同时负责将厨师做好的菜送到客人面前

创建store.js文件

import { createStore } from 'redux';  // 从redux中取出创建仓库函数
import Reducer from './count_reducers'; // 引入加工的reducer
export default createStore(Reducer); // 创建实例同时传入reducer并暴露加工后的reducer值

Reducers(厨师):将菜单上的做菜方式和客人选的菜做好后交给老板,他有两个参数 preState,action

preState:上一个值,也可以直接在形参中初始值

action:接收到客人的action对象,其中有type和data

创建count_reducer.js文件

import { INCREMENT, DELCREMENT } from './constants';

export default function Reducer(initPreState = 0, action) {
    const { type, data } = action;
    switch (type) {
        case INCREMENT:
            return initPreState + data * 1;
        case DELCREMENT:
            return initPreState - data * 1;
        default:
            return initPreState;
    }
}

最终React Components (react组件)希望拿到reducers计算后的值就需要从Store身上获取

const count = store.getState() // 取值
store.getState() // 页面显示

页面更新(订阅):subscribe(注意别写成subscript)

两种方式  
    1、在使用的组件中用ComponentDidMount生命周期中更新
    store.subscribe(()=>{
        this.setState({})
    })
	2、一劳永逸,直接在index.js react入口文件的渲染root根标签外部用store.subscribe包裹,好处是整个root内部的所有文件的store都有实时数据更新的效果
	store.subscribe(() => {
    	root.render(
            <React.StrictMode>
                <App />
            </React.StrictMode>
    	);
	});

3、优化:

1、action常量集

创建constants.js

作用:一般用于reducers中使用,如果需要更改方法直接在constants.js常量集 中修改type类型,

constants.js
export const INCREMENT = 'inCrement'

例如现在在reducers.js中
import { INCREMENT, DELCREMENT } from './constants';

export default function Reducer(initPreState = 0, action) {
    const { type, data } = action;
    switch (type) {
        case INCREMENT:
            return initPreState + data * 1;
        case DELCREMENT:
            return initPreState - data * 1;
        default:
            return initPreState;
    }
}
2、封装创建action方法常量集

创建count_action.js 作用:封装一个action对象

import { INCREMENT } from './constants';
export const createIncrementAction = data => ({ type: INCREMENT, data })

使用的页面需要引入封装好的action方法
import { createIncrementAction } from '../../redux/count_action';
示例:
	 store.dispatch({ type: 'delCrement', data: value });  // 这是没封装前的传参
      store.dispatch(createDelcrementAction(value)); // 使用封装的action

4、传递异步action

action有两种方式传参
第一种:传对象 { type: 'inCrement', data }   同步的
第二种: 传函数 异步的   延迟操作或者接口不喜欢在组件内调用就可以统一写在封装count_action.js集里面
// 引入redux自带的异步中间件  yarn add redux-thunk
// 为啥要下载这个插件? 因为redux异步需要中间件的支持才能传递action,所以这是必经之路。
// 在store.js中导入取出,同时从redux中取出应用中间件函数 applyMiddleware
import { createStore, applyMiddleware } from 'redux';  
import thunk from 'redux-thunk'; 
// 然后在暴露仓库的参数中应用一下,将第三方插件引入,这样整个store仓库就可以接收和返回异步action了
export default createStore(Reducer, applyMiddleware(thunk));

count_action.js   
export const createIncrementAction = data => ({ type: INCREMENT, data });
export const createIncrementAsyncAction = (data, delay) => {
    return dispatch => {
        setTimeout(() => {
            // 由于我们需要的方法其他action已经有了,所以可以直接使用,只需要将我们需要传的参数传入即可
            // 分发有两种写法  
            	// 一种是顶部引入store.js文件
               // store.dispatch(createIncrementAction(data))
               // 第二种是直接在当前return 这个函数体中本身就是在返回store对象,所以可以从形参dispatch直接				传,这样就少写store和少导入store.js文件。
            dispatch(createIncrementAction(data));
        }, delay);
    };
};

组件中使用
导入
import { createIncrementAsyncAction } from '../../redux/count_action';
 asyncCrement = () => {
        const { value } = this.selectState;
        store.dispatch(createIncrementAsyncAction(value, 500));
    };

二、React-redux (facebook)

1、安装 yarn add react-redux

2、工作原理图解析

redux与react-redux学习笔记_第2张图片

容器组件:主要存放容器UI,它自身拥有操作store中的状态和数据的方法api

容器UI:通过this.props获取到容器组件传递过来的store数据进行页面渲染

创建 containers 文件夹并在下面创建 Count 文件 -> index.js (存放容器组件的)

containers -> Count -> index.js
// 引入容器UI
import CountUI from '../../components/Count';
// 取出connect 容器UI 和 容器组件的连接器
import { connect } from 'react-redux';
// 引入封装好的action方法
import {
    createIncrementAction,
    createDelcrementAction,
    createIncrementAsyncAction,
} from '../../redux/count_action';

// 修改store数据   形参是store中的reducer处理后返回给它的数据赋值给count属性,再将count属性传递给子组件,子组件Count就可以通过this.props.count获取到store中的数据了
const mapStateToProps = state => {
    return { count: state };
};

// 分发action到store去修改状态
const mapDispatchToProps = dispatch => {
    return {
        jia: number => dispatch(createIncrementAction(number)),
        jian: number => dispatch(createDelcrementAction(number)),
        jiaAsync: (number, delay) => dispatch(createIncrementAsyncAction(number, delay)),
    };
};

// 连接器需要通过调用两个函数并传入相关参数,
// 第一个函数有两个参数   
// 两个参数都是得传{}形式,一个映射取数据,另一个是映射发送action到store让reducer去修改状态
// 第二个函数是传入一个容器UI从而生成一个容器组件
// 整句代码的意思是传入要加工的action和导入组件并创建暴露出容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);

在App.jsx 入口文件引入 Count 容器组件用于渲染,为了让store的数据给Count 容器组件内的容器UI都能使用,需要引入并在组件中传递

App.jsx
import React, { Component } from 'react';
import Count from './containers/Count'; // 容器组件
import store from './redux/store'; // 1

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

component(存放容器UI)

component -> count -> index.jsx  当前容器UI 
inCrement = () => {
        const { value } = this.selectState;
     	// 通过this.props取值
        this.props.jia(value);
    };

3、优化

1、容器组件代码 connect第一个回调传递有两种方式
/* 第一种  用函数变量形式传入
修改store数据   形参是store中的reducer处理后返回给它的数据赋值给count属性,再将count属性传递给子组件,子组件Count就可以通过this.props.count获取到store中的数据了
*/
const mapStateToProps = state => {
    return { count: state };
};

// 分发action到store去修改状态
const mapDispatchToProps = dispatch => {
    return {
        jia: number => dispatch(createIncrementAction(number)),
        jian: number => dispatch(createDelcrementAction(number)),
        jiaAsync: (number, delay) => dispatch(createIncrementAsyncAction(number, delay)),
   };
};
// 创建一个容器组件并链接容器UI   传入两个映射函数变量
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);

/* 第二种   利用react-redux自带的dispatch以及语法优化让代码更简洁,当然也需要看得懂才行
 创建一个容器组件并链接容器UI  
 第一个参数 直接将state表达式写入connect()
 第二个参数 用对象key: value的形式传入,直接调用封装好的action函数就可以,这个参数对象react-redux会自动加上dispatch,所以不用再手动写dispatch */
export default connect(
    state => ({ count: state }), 
    {jia: createIncrementAction,
     jian: createDelcrementAction,
     jiaAsync: createIncrementAsyncAction})
	(CountUI);

2、 引入Provider并在标签中传入store,这样App下的所有组件都可以使用到store的状态和数据了
优化前:
redux还需要在index.js中使用store.subscribe让所有App下的应用使用store都能实时更新,同时哪些容器组件,例如Count中要给自身的容器UI传store还要在容器组件Count中进行引入和传值
缺点:如果多个容器组件那么需要给每个容器组件传store,见下面的ForExample1、ForExample2
App.jsx
import Count from './containers/Count';
import store from './redux/store';
export default class App extends Component {
    render() {
        return (
            <div>
                <Count store={ store } />
                <ForExample1 store={ store } />
                <ForExample2 store={ store } />
            </div>
        );
    }
}


优化后:
项目根目录入口文件  index.js
import store from './redux/store';
import { Provider } from 'react-redux';

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

3、容器组件和容器UI整合版

优化目的:

原本每生成一个容器组件就需要创建容器组件和容器UI文件,1 -> 2,量多文件就翻倍增长

容器UI:components -> Count -> index.jsx

容器组件:contaners -> Count -> index.jsx

优化后代码:

constaners -> Count -> index.jsx
/* 
    四个步骤:
    1、创建容器UI
    2、导入react-redux连接器 connect
    3、连接容器UI创建并生成容器组件暴露出去
    4、连接时传入映射状态和映射操作方法
*/
import React, { Component } from "react";
import { connect } from "react-redux"; // 2
import { createIncrementAction } from "../../redux/count_action";

// 1
class Count extends Component {
  increment = () => {
    this.props.incrementFN(1); 
  };

  render() {
    return (
      <div>
        <h2>当前计算后的值 {this.props.sumCount} </h2>
        <button onClick={this.increment}>1</button>
      </div>
    );
  }
}
// 3 4
export default connect((state) => ({ sumCount: state }), {
  incrementFN: createIncrementAction,
})(Count);

为了让每个新增的对象都拥有唯一的ID,所以可以使用第三方插件nanoid,当然也可以用时间戳或者其他方式

安装命令 yarn add nanoid
如何使用
import { nanoid } from 'nanoid'
直接调用即可  <div> { nanoid() } </div>

combindReducer:可以用对象形式合并多个reducer为一个总reducer,统一发给store进行操作,让redux不再只为一个reducer服务

从redux引入  用对象key:value的形式传入,这样store就可以保存所有状态了 
store.js
import { createStore, applyMiddleware, combineReducers } from 'redux'
import countReducer from '../redux/reducers/count';
import personReducer from '../redux/reducers/person';

export default createStore(
    combineReducers({
        count: countReducer,
        person: personReducer
    })
    , applyMiddleware(thunk));

页面获取store中的状态、

以Person容器组件为例
connect连接器保存着组件的状态,state就是当前容器组件保存在store中的数据,当前容器组件还可以获取其他容器组件的状态。这一过程实现了状态数据共享。
export default connect(
  (state) => ({ renArr: state.person, countSum: state.count }),
  {
    jiayiren: createPersonAction,
  }
)(Person);
4、react redux 开发工具的使用
store.js
/* 下载命令  yarn add redux-devtools-extension
引入  import { composeWithDevTools } from 'redux-devtools-extension'
将 composeWithDevTools 整个react-redux开发工具包裹整个异步action
composeWithDevTools(applyMiddleware(thunk)) */

export default createStore(  
    combineReducers({
        he: countReducer,
        rens: personReducer
    })
    , composeWithDevTools(applyMiddleware(thunk)));

5、将所有容器组件的reducer汇总到一个js文件中,store使用reducer就直接引入这个js文件进行操作

优点:这样如果需要修改reducer或者新增reducer就只需在reducer -> index.js中进行操作,同时store.js中也无需引入大量的reducer,代码更清晰、后期更好维护、更人性化。

reducer -> index.js
在reducer下创建index.js,在该文件中引入所有容器组件的reducer,最终统一暴露一个汇总后的reducer对象

import count from './count';
import person from './person';
// 引入整合reducer方法
import { combineReducers } from 'redux';
export default combineReducers({count, person})

store.js
// 引入统一汇总后的reducer
import reducer from './reducers'
export default createStore(reducer, composeWithDevTools(applyMiddleware(thunk)));

你可能感兴趣的:(React,javascript,前端框架,前端,react.js,npm)