热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

redux的学习以及初探redux的API实现:redux基础知识

本文主要介绍关于javascript,前端,reactjs,react.js,ecmascript的知识点,对【redux的学习以及初探redux的API实现】和【redux基础知识】有兴趣的朋友可以

本文主要介绍关于Javascript,前端,reactjs,react.js,ecmascript的知识点,对【redux的学习以及初探redux的API实现】和【redux基础知识】有兴趣的朋友可以看下由【尤雨东】投稿的技术文章,希望该技术和经验能帮到你解决你所遇的react相关技术问题。

redux基础知识

learn redux redux应用场景 随着Javascript单页应用开发日趋复杂,管理不断变化的state 非常困难Redux的出现就是为了解决state里的数据问题在React中,数据在组件中是单向流动的数据从一个方向父组件流向子组件(通过props),由于这个特征,两个非父子关系的组件(或者称作兄弟组件)之间的通信就比较麻烦 redux设计思想 Redux是将整个应用状态存储到到一个地方,称为store里面保存一棵状态树state tree组件可以派发dispatch行为action给store,而不是直接通知其它组件其它组件可以通过订阅store中的状态(state)来刷新自己的视图 createStore

使用createStore方法可以创建一个store,该函数的参数是reducer和initialState。

常见的三个方法:

dispatchsubscribegetState

我们就以计数器案例来学习createStore

计数器案例
 import { createStore } from "redux";
 import { Component } from "react";
 ​
 const ADD = "add";
 const MINUS = "minus";
 /**
  * 纯函数
  * @param {*} state 老状态
  * @param {*} action 动作 必须有一个type属性
  */
 function reducer(state = { counter: 0 }, action) {
   switch (action.type) {
     case ADD:
       return { counter: state.counter + 1 };
     case MINUS:
       return { counter: state.counter - 1 };
     default:
       return state;
   }
 }
 const store = createStore(reducer, { counter: 0 });
 ​
 console.log(store);
 ​
 class App extends Component {
   state = {
     // getState 获取store的状态
     counter: store.getState().counter,
   };
   componentDidMount() {
     // 订阅状态的变化
     store.subscribe(() => {
       // 状态更新 以后 执行该回调
       this.setState({
         counter: store.getState().counter,
       });
       console.log(store.getState())
     });
   }
   add = () => {
     store.dispatch({ type: ADD });
   };
   minus = () => {
     store.dispatch({ type: MINUS });
   };
   render() {
     return (
       
  
         

counter: {this.state.counter}

                           
    );   }  }  ​  export default App;  ​
createStore的实现原理

首先:我们需要创建一个createStore函数,接收两个参数:纯函数参数reducer以及初始化状态initialState

 /** * 创建一个 store * @param {*} reducer 纯函数 计算新状态的处理器 * @param {*} initialState 初始状态 */
 function createStore(reducer, initialState) {
   
     // 在仓库内部定义一个初始的状态
     let state = initialState;
 }

接下来我们需要定义三个函数:也就是上面我们说的,常用的三个函数。

 function createStore(reducer, initialState) {
   
   // 在仓库内部定义一个初始的状态
   let state = initialState;
   /** * 获取当前仓库最新的状态 */
   function getState() {
   
     return state;
   }
   /** * 订阅状态的更新 * @param {*} listener 状态更新后执行的监听函数 * @returns 返回一个可以取消监听函数的方法 */
   function subscribe(listener) {
   
   }
   /** * 派发 更新状态 * @param {*} action 动作 */
   function dispatch(action) {
   
   }
   return {
   
     getState,
     dispatch,
     subscribe,
   };
 }

getState函数就是返回当前最新的状态,没什么好说的。

dispatch派发一个动作来更新当前的state:

   function dispatch(action) {
   
     state = reducer(state, action);
   }

subscribe的参数是一个监听函数,在state的值更新以后调用。所以我们需要一个数组来记录保存的listener,然后该函数的返回值是一个取消该监听函数的一个函数。

   // 监听函数
   let listeners = [];
   function subscribe(listener) {
   
     listeners.push(listener);
     // 返回一个可以取消监听函数的方法
     return () => {
   
       listeners = listeners.filter((l) => l !== listener);
     };
   }

在状态更新以后,我们执行监听函数。

   function dispatch(action) {
   
     state = reducer(state, action);
     // 执行监听函数
     listeners.forEach((listener) => listener());
   }

此时我们可以把redux换成自己写的这个createStore,发现可以达到一样的效果。

 /** * 组件和仓库有两种关系: * 1. 输入 组件可以从仓库中读取状态数据进行渲染和显示 * 2. 输出 可以在组件派发动作,修改仓库中的状态 */
 const Counter = () => {
   
   const [counter, setCounter] = useState(store.getState());
   useEffect(() => {
   
     const unsubscribe = store.subscribe(() => {
   
       setCounter(store.getState());
     });
     return unsubscribe;
   }, []);
   return (
     <div>
       <h2>counter: {
   counter.counter}</h2>
       <button onClick={
   () => store.dispatch({
    type: ADD })}>+1</button>
       <button onClick={
   () => store.dispatch({
    type: MINUS })}>-1</button>
     </div>
   );
 };

当然,这样写可能我们还是觉得没有那么优雅,如果派发action的时候,只需要给点击按钮一个函数,就自动帮我们派发,不需要额外的动作就更好了。

bindActionCreators

实现自动派发,我们可以使用redux提供的bindActionCreators方法。该方法的参数是actionCreatordispatch方法。

 const add = () => ({
    type: ADD });
 const minus = () => ({
    type: MINUS });
 const actionCreators = {
    add, minus };
 // 把一个action创建者对象和store.dispatch 方法进行绑定 返回一个对象
 const boundActions = bindActionCreators(actionCreators, store.dispatch);
 // ....
       <button onClick={
   boundActions.add}>+1</button>
       <button onClick={
   boundActions.minus}>-1</button>

这样就可以实现自动派发action了。

那么:bindActionCreators是如何实现的?

其实就是对派发的函数进行一次封装,actionCreator就是对action的封装,该对象的每个函数的执行结果都是一个action对象,然后把该对象传递给dispatch方法即可.

 /** * 绑定actionCreator和dispatch 实现自动派发 * @param {*} actionCreator * @param {*} dispatch */
 function bindActionCreators(actionCreator, dispatch) {
   
   // 绑定的自动派发对象
   const boundActionCreators = {
   };
   for (const key in actionCreator) {
   
     const ac = actionCreator[key];
     // 绑定的函数
     boundActionCreators[key] = bindActionCreator(ac, dispatch);
   }
   return boundActionCreators;
 }function bindActionCreator(actionCreator, dispatch) {
   
   return (...args) => {
   
     // 、派发action 也可以接收传入的参数
     return dispatch(actionCreator(...args));
   };
 }
combineReducers

React应用不管多大,只能有一个仓库,只能有一个reducer,reducer只能维护一个状态。那就可能导致我们一个reducer里面有上百个switch case,维护起来很难受。

因此:我们可以考虑把reducer拆分,一个唯一的reducer,由很多个小的reducer组合而成。也就分模块。

action-type

 export const ADD = "add";
 export const ADDNUM = "addNum";
 export const MINUS = "minus";export const ADD2 = "add2";
 export const ADDNUM2 = "addNum2";
 export const MINUS2 = "minus2";

counter1 reducer:

 import {
    ADD, MINUS, ADDNUM } from "../action-type";
 /** * 纯函数 * @param {*} state 老状态 * @param {*} action 动作 必须有一个type属性 */
 function reducer(state = {
     counter: 0 }, action) {
   
   switch (action.type) {
   
     case ADD:
       return {
    counter: state.counter + 1 };
     case ADDNUM:
       return {
    counter: state.counter + action.num };
     case MINUS:
       return {
    counter: state.counter - 1 };
     default:
       return state;
   }
 }export default reducer;

counter2 reducer

 import {
    ADD2, MINUS2, ADDNUM2 } from "../action-type";
 function reducer(state = {
     counter: 0 }, action) {
   
   switch (action.type) {
   
     case ADD2:
       return {
    counter: state.counter + 1 };
     case ADDNUM2:
       return {
    counter: state.counter + action.num };
     case MINUS2:
       return {
    counter: state.counter - 1 };
     default:
       return state;
   }
 }export default reducer;

合并reducer:借助redux提供的 combineReducers方法

 import counter1 from "./counter1";
 import counter2 from "./counter2";
 import {
    combineReducers } from "redux";// 合并多个reducer
 const reducers = {
    counter1, counter2 };
 const combineReducer = combineReducers(reducers);export default combineReducer;
 import { createStore } from "../redux";
 import combineReducer from "./reducers";
 const store = createStore(combineReducer);
 export default store;
 export * from "./action-type";
 ​

actionCreators:

 import {
    ADD, MINUS, ADDNUM } from "../action-type";
 // actionCreators
 const add = () => ({
    type: ADD });
 const addNum = (num) => ({
    type: ADDNUM, num });
 const minus = () => ({
    type: MINUS });
 const actionCreators = {
    add, addNum, minus };
 export default actionCreators;
 import {
    ADD2, MINUS2, ADDNUM2 } from "../action-type";
 // actionCreators
 const add2 = () => ({
    type: ADD2 });
 const addNum2 = (num) => ({
    type: ADDNUM2, num });
 const minus2 = () => ({
    type: MINUS2 });
 const actionCreators2 = {
    add2, addNum2, minus2 };
 export default actionCreators2;

两个计数器之间互不干扰:

 import store from "./store";
 import { useEffect, useState } from "react";
 import { bindActionCreators } from "./redux";
 import actionCreators from "./store/actionCreator/counter";
 import actionCreators2 from "./store/actionCreator/counter2";
 ​
 // 把一个action创建者对象和store.dispatch 方法进行绑定 返回一个对象
 const boundActiOns= bindActionCreators(actionCreators, store.dispatch);
 /**
  * 组件和仓库有两种关系:
  * 1. 输入 组件可以从仓库中读取状态数据进行渲染和显示
  * 2. 输出 可以在组件派发动作,修改仓库中的状态
  */
 const Counter = () => {
   const [counter, setCounter] = useState(store.getState().counter1);
   useEffect(() => {
     const unsubscribe = store.subscribe(() => {
       setCounter(store.getState().counter1);
     });
     return unsubscribe;
   }, []);
   return (
     
  
       

counter: {counter.counter}

                             
  );  };  const boundActions2 = bindActionCreators(actionCreators2, store.dispatch);  const Counter2 = () => {    const [counter, setCounter] = useState(store.getState().counter2);    useEffect(() => {      const unsubscribe = store.subscribe(() => {        setCounter(store.getState().counter2);     });      return unsubscribe;   }, []);    return (      
       

counter: {counter.counter}

                             
  );  };  ​  const App = () => {    return (      <>                
                );  };  ​  export default App;
combineReducers的原理

该方法是组合多个reducer为一个reducer。参数是一个多个reducer集合的对象,且对多个reducer之前分模块,状态也分成的多个模块,其模块名就是reducer集合对象的key。

 /** * 合并多个reducer * @param {
   {[key:string]:Function}} reducers {counter1: reducer1 } */
 export function combineReducers(reducers) {
   
   // 返回一个全局唯一的reducer函数 state是每次传入的上次的state
   return function (state = {
    }, action) {
   
     let nextState = {
   };
     for (const key in reducers) {
   
       const reducer = reducers[key];
       // 老状态
       const lastStateForKey = state[key];
       // 计算新状态
       nextState[key] = reducer(lastStateForKey, action);
     }
     return nextState;
   };
 }

本文《redux的学习以及初探redux的API实现》版权归尤雨东所有,引用redux的学习以及初探redux的API实现需遵循CC 4.0 BY-SA版权协议。


推荐阅读
author-avatar
echo7111436
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有