React----redux&&react-redux

293 阅读10分钟

redux

redux原理图.png

求和redux_mini版

一、去除组件自身的状态,交给redux来管理

二、在src文件下创建redux文件夹,在该文件夹中创建store.jscount_reducer.js两个文件

三、store.js

  • 引入redux中的createStore函数,创建一个store

  • createStore调用时传入一个为其服务的reducer

  • 记得暴露store对象

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

// 引入createStore,专门用于创建redux中最为核心的store对象
import { createStore } from "redux";

// 引入为Count组件服务的Reducer
import countReducer from "./count_reducer";

// 暴露store
const store = createStore(countReducer);
export default store;

四、count_reducer.js

  • reducer的本质是一个函数,接收两个参数:preState,action,并且返回一个加工后的状态

  • reducer有两个作用:初始化状态,加工状态

  • reducer第一次被调用,是store自动触发的,传递的preStateundefined

count_reducer.js// 该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
// reducer函数会接收两个参数,分别是之前的状态(preState),动作对象(action)

// 设置初始值
const initState = 0;

export default function countReducer(preState = initState, action) {
  // console.log(preState, action);
  // 从action中获取动作类型type和数据data
  const { type, data } = action;
  // 根据type判断如何加工数据
  switch (type) {
    case "increment":
      return preState + data * 1;
    case "decrement":
      return preState - data * 1;
    default:
      return preState;
  }
}

五、api:

  • store.getState():用于获取数据

  • store.dispatch({type:'xxx',data:xxx}):用来使用定义的方法

  • store.subscribe(()=>{}):用来数据更新后render

count.jsx//使用
  const increment = () => {
    store.dispatch({
      type: "increment",
      data: value * 1,
    });
  };
  
  //获取数据
 <h1>当前求和为:{store.getState()}</h1>
 
 
 
 index.js:
 
 // 用于数据更新后render
store.subscribe(() => {
  ReactDOM.render(<App />, document.getElementById("root"));
});

完整版

基于上面的迷你版,增加了两个文件:

  • count_action.js:用来生成action,交给store.dispatch使用

  • constant.js:用来定义变量方便管理和防止程序员单词写错

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

// 引入createStore,专门用于创建redux中最为核心的store对象
import { createStore } from "redux";
// 引入为Count组件服务的Reducer
import countReducer from "./count_reducer";

// 暴露store
const store = createStore(countReducer);
export default store;



count_reducer.js// 该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
// reducer函数会接收两个参数,分别是之前的状态(preState),动作对象(action)

// 设置初始值
import { INCREMENT, DECREMENT } from "./constant";
const initState = 0;

export default function countReducer(preState = initState, action) {
  // console.log(preState, action);
  // 从action中获取动作类型type和数据data
  const { type, data } = action;
  // 根据type判断如何加工数据
  switch (type) {
    case INCREMENT:
      return preState + data * 1;
    case DECREMENT:
      return preState - data * 1;
    default:
      return preState;
  }
}


constant.js:

// 该文件是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错

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



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

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

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



count.jsximport store from "../../redux/store";

import {
  createIncrementAction,
  createDecrementAction,
} from "../../redux/count_action";


  //使用
  const increment = () => {
    store.dispatch(createIncrementAction(value * 1));
  };
  const decrement = () => {
    store.dispatch(createDecrementAction(value * 1));
  };
  
  //获取数据
 <h1>当前求和为:{store.getState()}</h1>
 
 
 
 index.js:
 
 // 用于数据更新后render
store.subscribe(() => {
  ReactDOM.render(<App />, document.getElementById("root"));
});

实现异步action

  • 当延迟的动作不想交给组件自身,想交给action

  • 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回

  • 具体编码:

    • yarn add redux-thunk,并配置在store中,使用redux中的中间件applyMiddleware

    • 创建action的函数不在返回一个对象,而是一个函数,该函数中写异步任务

    • 异步任务有结果后,分发一个同步的action去真正操作数据

  • 备注:异步action不是必须要写,完全可以直接等待异步任务结束再去分发同步action

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

// 引入createStore,专门用于创建redux中最为核心的store对象
import { createStore, applyMiddleware } from "redux";
// 引入为Count组件服务的Reducer
import countReducer from "./count_reducer";
import thunk from "redux-thunk";

// 暴露store
const store = createStore(countReducer, applyMiddleware(thunk));
export default store;




count_reducer.js// 该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
// reducer函数会接收两个参数,分别是之前的状态(preState),动作对象(action)

// 设置初始值
import { INCREMENT, DECREMENT } from "./constant";
const initState = 0;

export default function countReducer(preState = initState, action) {
  // console.log(preState, action);
  // 从action中获取动作类型type和数据data
  const { type, data } = action;
  // 根据type判断如何加工数据
  switch (type) {
    case INCREMENT:
      return preState + data * 1;
    case DECREMENT:
      return preState - data * 1;
    default:
      return preState;
  }
}


constant.js:

// 该文件是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错

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



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


// 同步action就是指action的值是object类型的一般对象
export const createIncrementAction = (data) => ({ type: INCREMENT, data });
export const createDecrementAction = (data) => ({ type: DECREMENT, data });


// 异步action就是指action的值是函数,异步action中一般都会调用同步action
export const createIncrementAsyncAction = (data, time) => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(createIncrementAction(data));
    }, time);
  };
};




count.jsximport store from "../../redux/store";

import {
  createIncrementAction,
  createDecrementAction,
} from "../../redux/count_action";


  //使用
  const increment = () => {
    store.dispatch(createIncrementAction(value * 1));
  };
  const decrement = () => {
    store.dispatch(createDecrementAction(value * 1));
  };
  
  //获取数据
 <h1>当前求和为:{store.getState()}</h1>
 
 
 
 index.js:
 
 // 用于数据更新后render
store.subscribe(() => {
  ReactDOM.render(<App />, document.getElementById("root"));
});

react-redux

react-redux模型图.png

基本使用

一、将CountUI组件中与redux的使用全部删除

二、创建Count容器组件:

  • 引入Count的UI组件

  • 引入connect用于连接UI组件和redux中store

  • 使用connect()()创建容器组件CountContainer

  • 将容器组件暴露出去

// 引入Count的UI组件
import CountUI from "../../components/Count";

// 引入connect用于连接UI组件和redux中store
import { connect } from "react-redux";

//使用connect()()创建容器组件CountContainer
const CountContainer = connect()(CountUI);

// 将容器组件暴露出去
export default CountContainer;

三、在App.js中不再引入CountUI组件而是引入Count容器组件,再引入store,将store通过props传递

import React from "react";
// import Count from "./components/Count";
// 引入容器组件
import Count from "./containers/Count";
import store from "./redux/store";
export default function App() {
  return (
    <>
      <Count store={store} />
    </>
  );
}

四、在Count容器组件中定义两个方法,并且将这两个方法传递给connect方法中

  • mapStateToProps():将redux中的状态传递给CountUI组件

  • mapDispatchToProps():将操作状态的方法传递给CountUI组件

// 引入Count的UI组件
import CountUI from "../../components/Count";

// 引入connect用于连接UI组件和redux中store
import { connect } from "react-redux";

//引入action
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/count_action";

// 该函数中返回的对象是传递给CountUI组件的数据,相当于<CountUI n={900} />
// react-redux在调用该函数时会自动调用store.getState()函数,并且将返回的值传递给了该函数,即state
function mapStateToProps(state) {
  return { count: state };
}

// 该函数中返回的对象是传递给CountUI组件的方法,相当于<CountUI jia={()=>{}} />
//同样也会接收一个参数,该参数也是由react-redux调用传递过来的,用来操作store中的方法,即dispatch
function mapDispatchToProps(dispatch) {
  return {
    jia: (data) => dispatch(createIncrementAction(data)),
    jian: (data) => dispatch(createDecrementAction(data)),
    jiaAsync: (data, time) => dispatch(createIncrementAsyncAction(data, time)),
  };
}

//使用connect()()创建容器组件CountContainer
const CountContainer = connect(mapStateToProps, mapDispatchToProps)(CountUI);

// 将容器组件暴露出去
export default CountContainer;

五、CountUI组件可以从props接收到Count容器组件传递过来的数据和方法,即redux中的数据和操作redux中数据的方法

const { store, count, jia, jian, jiaAsync } = props;


const increment = () => {
    jia(value);
};

<h1>当前求和为:{count}</h1>

优化

mapDistatchToProps简化

初始:

// 该函数中返回的对象是传递给CountUI组件的数据,相当于<CountUI n={900} />
// react-redux在调用该函数时会自动调用store.getState()函数,并且将返回的值传递给了该函数,即state
function mapStateToProps(state) {
  return { count: state };
}

// 该函数中返回的对象是传递给CountUI组件的方法,相当于<CountUI jia={()=>{}} />
//同样也会接收一个参数,该参数也是由react-redux调用传递过来的,用来操作store中的方法,即dispatch
function mapDispatchToProps(dispatch) {
  return {
    jia: (data) => dispatch(createIncrementAction(data)),
    jian: (data) => dispatch(createDecrementAction(data)),
    jiaAsync: (data, time) => dispatch(createIncrementAsyncAction(data, time)),
  };
}

//使用connect()()创建容器组件CountContainer
const CountContainer = connect(mapStateToProps, mapDispatchToProps)(CountUI);

// 将容器组件暴露出去
export default CountContainer;



简写:

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精简写法
  {
    jia: createIncrementAction,
    jian: createDecrementAction,
    jiaAsync: createIncrementAsyncAction,
  }
)(CountUI);

export default CountContainer;

Provider

  • 在index.js文件中不用再使用store.subscribe()方法,react-redux会自动检测

  • 在App.jsx文件中不需要挨个组件传递store,而是在index.js中使用react-redux中的Provider来自动精准分配store

App.jsximport React from "react";
// 引入容器组件
import Count from "./containers/Count";

export default function App() {
  return (
    <>
      <Count />
    </>
  );
}


index.js:

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

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

整合组件

将CountUI组件和Count容器组件整合在同一个文件中

import { connect } from "react-redux";

import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/count_action";

import React, { useState } from "react";

//CountUI组件
function Count(props) {
  ......
}

//Count容器组件
const CountContainer = connect(
  (state) => ({ count: state }),
  {
    jia: createIncrementAction,
    jian: createDecrementAction,
    jiaAsync: createIncrementAsyncAction,
  }
)(Count);

export default CountContainer;

数据共享

一、创建Count、Person的相对应的action和reducer

  • Count:
action:

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

// 同步action就是指action的值是object类型的一般对象
export const createIncrementAction = (data) => ({ type: INCREMENT, data });

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

// 异步action就是指action的值是函数,异步action中一般都会调用同步action
export const createIncrementAsyncAction = (data, time) => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(createIncrementAction(data));
    }, time);
  };
};


reducer:

// 该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
// reducer函数会接收两个参数,分别是之前的状态(preState),动作对象(action)

// 设置初始值
import { INCREMENT, DECREMENT } from "../constant";
const initState = 0;

export default function countReducer(preState = initState, action) {
  // 从action中获取动作类型type和数据data
  const { type, data } = action;
  // 根据type判断如何加工数据
  switch (type) {
    case INCREMENT:
      return preState + data * 1;
    case DECREMENT:
      return preState - data * 1;
    default:
      return preState;
  }
}

  • Person:
action:

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

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



reducer:

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

const initState = [{ name: "tom", age: 18 }];
export default function personReducer(preState = initState, action) {
  const { type, data } = action;
  switch (type) {
    case ADD_PERSON:
      return [...preState, data];
    default:
      return preState;
  }
}

二、定义变量

// 该文件是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错

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

三、将countReducer和personReducer整合

  • 使用react-redux中的combineReducers整合,写成对象的形式
// 该文件专门用于暴露一个store对象,整个应用只有一个store对象

// 引入createStore,专门用于创建redux中最为核心的store对象
import { createStore, applyMiddleware, combineReducers } from "redux";
// 引入为Count组件服务的Reducer
import countReducer from "./reducers/count";
import personReducer from "./reducers/person";
import thunk from "redux-thunk";


// 整合全部reducer变成一个总的reducer
const allReducer = combineReducers({
  count: countReducer,
  personList: personReducer,
});


// 暴露store
const store = createStore(allReducer, applyMiddleware(thunk));
export default store;

四、创建Count,Person容器组件和UI组件

Count.jsx:

import { connect } from "react-redux";

import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/actions/count";

import React, { useState } from "react";

//CountUI组件
function Count(props) {
    ......
}

//Count容器组件
const CountContainer = connect(

  //获取redux中的count和personList数据
  (state) => ({ count: state.count, personList: state.personList }),
  {
    jia: createIncrementAction,
    jian: createDecrementAction,
    jiaAsync: createIncrementAsyncAction,
  }
)(Count);

export default CountContainer;


Person.jsximport React, { useState } from "react";
import { connect } from "react-redux";
import { createAddPersonAction } from "../../redux/actions/person";

//PersonUI组件
function Person(props) {
    ......
}

//Person容器组件
export default connect(

   //获取redux中的count和personList数据
  (state) => ({
    personList: state.personList,
    count: state.count,
  }),
  {
    add: createAddPersonAction,
  }
)(Person);

六、此时两边的组件都可以相互读取各自存放在redux中的数据

image.png

redux开发者工具

一、谷歌浏览器安装插件

二、在项目的终端中运行yarn add redux-devtools-extension

三、在store.js文件中引入并使用

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

// 引入createStore,专门用于创建redux中最为核心的store对象
import { createStore, applyMiddleware, combineReducers } from "redux";
// 引入为Count组件服务的Reducer
import countReducer from "./reducers/count";
import personReducer from "./reducers/person";
import thunk from "redux-thunk";

//引入开发者工具
import { composeWithDevTools } from "redux-devtools-extension";

// 整合全部reducer变成一个总的reducer
const allReducer = combineReducers({
  count: countReducer,
  personList: personReducer,
});


// 暴露store
const store = createStore(
  allReducer,
  //使用开发者工具
  composeWithDevTools(applyMiddleware(thunk))
  
);
export default store;