React中理解並使用Redux

1.爲何要使用redux

在這裏插入圖片描述

  1. 像父子組件之間相互傳值相互調用的狀況,而且值的適用範圍僅限於父子組件之間,這時不須要使用Redux.
  2. 當某個子組件去更新某種狀態時,好比更新組織機構數據。而其餘的頁面又須要依賴這些數據時,此時能夠考慮使用redux,把這些狀態值放入到redux中進行管理。
2.redux工做的過程圖

在這裏插入圖片描述

3.redux的工做流程圖

在這裏插入圖片描述

4.Redux簡介

Redux是針對JavaScript應用的可預測狀態容器javascript

  1. 可預測性(predictable)html

  2. 狀態容器(state container)java

  3. JavaScript應用node

在這裏插入圖片描述

首先咱們須要弄清Redux模型中的幾個組成對象:action 、reducer、store :react

  • action:官方的解釋是action是把數據從應用傳到 store 的有效載荷,它是 store 數據的惟一來源;要經過本地或遠程組件更改狀態,須要分發一個action;
  • reducer:action發出了作某件事的請求,只是描述了要作某件事,並無去改變state來更新界面,reducer就是根據action的type來處理不一樣的事件;
  • store:store就是把action和reducer聯繫到一塊兒的對象,store本質上是一個狀態樹,保存了全部對象的狀態。任何UI組件均可以直接從store訪問特定對象的狀態。

​ 在React中的組件是沒法直接更動state(狀態)的包含值,要透過setState方法來進行更動,這有很大的緣由是爲了Virtual DOM(虛擬DOM)的所設計,這是其中一點。另外在組件的樹狀階層結構,父組件(擁有者)與子組件(被擁有者)的關係上,子組件是隻能由父組件以props(屬性)來傳遞屬性值,子組件本身自己沒法更改本身的props,這也是爲何一開始在學習React時,都會看到大部份的例子只有在最上層的組件有state,並且都是由它來負責進行當數據改變時的從新渲染工做,子組件一般只有負責呈現數據。shell

​ 固然,有一個很技巧性的方式,是把父組件中的方法聲明由props傳遞給子組件,而後在子組件觸發事件時,調用這個父組件的方法,以此來達到子組件對父組件的溝通,間接來更動父組件中的state。不過這個做法並不直覺,須要事先規範好兩邊的方法。在簡單的應用程序中,這溝通方式還可行,但若是是在有複雜的組件嵌套階層結構時,例如層級不少或是不一樣樹狀結構中的子組件要互相溝通時,這個做法是派不上用場的。npm

​ 在複雜的組件樹狀結構時,惟一能做的方式,就是要將整個應用程序的數據整合在一塊兒,而後獨立出來,也就是整個應用程序領域的數據部份。另外還須要對於數據的全部更動方式,也要獨立出來。這二者組合在一塊兒,就是稱之爲」應用程序領域的狀態」,爲了區分組件中的狀態(state),這個做爲應用程序領域的持久性數據集合,會被稱爲store(存儲)。redux

說明:以上兩段來自慕課網對Redux的總結。數組

5.Redux簡單示例(轉載):

配置Redux開發環境的最快方法是使用create-react-app工具。在開始以前,確保已經安裝並更新了nodejs、npm和yarn。下面以生成一個redux-shopping項目並安裝Redux爲例。服務器

若是沒有安裝create-react-app工具,請使用下面的命令先執行安裝操做。

npm install -g create-react-app
複製代碼

而後,在使用下面的命令建立redux-shopping項目。

create-react-app redux-shopping
複製代碼

首先,刪除src文件夾中除index.js之外的全部文件。打開index.js,刪除全部代碼,鍵入如下內容:

import { createStore } from "redux";

const reducer = function(state, action) {
  return state;
}

const store = createStore(reducer);
複製代碼

上面代碼的意思是:

  1. 從redux包中引入createStore()方法;
  2. 建立了一個名爲reducer的方法,第一個參數state是當前保存在store中的數據,第二個參數action是一個容器,用於: type - 一個簡單的字符串常量,例如ADD, UPDATE, DELETE等。 payload - 用於更新狀態的數據。
  3. 建立一個Redux存儲區,它只能使用reducer做爲參數來構造。存儲在Redux存儲區中的數據能夠被直接訪問,但只能經過提供的reducer進行更新。

目前,state爲undefined或null,要解決這個問題,須要分配一個默認的值給state,使其成爲一個空數組。例如:

const reducer = function(state=[], action) {
  return state;
}
複製代碼

目前咱們建立的reducer是通用的,那麼咱們如何使用多個reducer呢?此時咱們可使用Redux包中提供的combineReducers函數。作以下內容修改:

// src/index.js

import { createStore } from "redux";
import { combineReducers } from 'redux';

const productsReducer = function(state=[], action) {
  return state;
}

const cartReducer = function(state=[], action) {
  return state;
}

const allReducers = {
  products: productsReducer,
  shoppingCart: cartReducer
}

const rootReducer = combineReducers(allReducers);

let store = createStore(rootReducer);
複製代碼

接下來,咱們將爲reducer定義一些測試數據。

// src/index.jsconst initialState = {
  cart: [
    {
      product: 'bread 700g',
      quantity: 2,
      unitCost: 90
    },
    {
      product: 'milk 500ml',
      quantity: 1,
      unitCost: 47
    }
  ]
}

const cartReducer = function(state=initialState, action) {
  return state;
}

…

let store = createStore(rootReducer);

console.log("initial state: ", store.getState());
複製代碼

接下來,咱們能夠在終端中執行npm start或者yarn start來運行dev服務器,並在控制檯中查看state。

這裏寫圖片描述

如今,咱們的cartReducer什麼也沒作,但它應該在Redux的存儲區中管理購物車商品的狀態。咱們須要定義添加、更新和刪除商品的操做(action)。此時咱們能夠作以下的一些定義:

// src/index.jsconst ADD_TO_CART = 'ADD_TO_CART';

const cartReducer = function(state=initialState, action) {
  switch (action.type) {
    case ADD_TO_CART: {
      return {
        ...state,
        cart: [...state.cart, action.payload]
      }
    }

    default:
      return state;
  }
}

…
複製代碼

咱們繼續來分析一下代碼。一個reducer須要處理不一樣的action類型,所以咱們須要一個SWITCH語句。當一個ADD_TO_CART類型的action在應用程序中分發時,switch中的代碼將處理它。

接下來,咱們將定義一個action,做爲store.dispatch()的一個參數。action是一個Javascript對象,有一個必須的type和可選的payload。咱們在cartReducer函數後定義一個:

function addToCart(product, quantity, unitCost) {
  return {
    type: ADD_TO_CART,
    payload: { product, quantity, unitCost }
  }
}
…
複製代碼

在這裏,咱們定義了一個函數,返回一個JavaScript對象。在咱們分發消息以前,咱們添加一些代碼,讓咱們可以監聽store事件的更改。

let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
);

unsubscribe();
複製代碼

接下來,咱們經過分發消息到store來向購物車中添加商品。將下面的代碼添加在unsubscribe()以前:

…
store.dispatch(addToCart('Coffee 500gm', 1, 250));
store.dispatch(addToCart('Flour 1kg', 2, 110));
store.dispatch(addToCart('Juice 2L', 1, 250));
複製代碼

下面是整個index.js文件的源碼:

// src/index.js

import { createStore } from "redux";
import { combineReducers } from 'redux';

const productsReducer = function(state=[], action) {
  return state;
}

const initialState = {
  cart: [
    {
      product: 'bread 700g',
      quantity: 2,
      unitCost: 90
    },
    {
      product: 'milk 500ml',
      quantity: 1,
      unitCost: 47
    }
  ]
}

const ADD_TO_CART = 'ADD_TO_CART';

const cartReducer = function(state=initialState, action) {
  switch (action.type) {
    case ADD_TO_CART: {
      return {
        ...state,
        cart: [...state.cart, action.payload]
      }
    }

    default:
      return state;
  }
}

function addToCart(product, quantity, unitCost) {
  return {
    type: ADD_TO_CART,
    payload: {
      product,
      quantity,
      unitCost
    }
  }
}

const allReducers = {
  products: productsReducer,
  shoppingCart: cartReducer
}

const rootReducer = combineReducers(allReducers);

let store = createStore(rootReducer);

console.log("initial state: ", store.getState());

let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
);

store.dispatch(addToCart('Coffee 500gm', 1, 250));
store.dispatch(addToCart('Flour 1kg', 2, 110));
store.dispatch(addToCart('Juice 2L', 1, 250));

unsubscribe();
複製代碼

保存代碼後,Chrome會自動刷新,能夠在控制檯中確認新的商品已經添加了。

這裏寫圖片描述

代碼拆分

會發現,index.js中的代碼逐漸變得冗雜。因此,接下來咱們對上面的項目進行一個組織拆分,使之成爲Redux項目。首先,在src文件夾中建立一下文件和文件夾,文件結構以下:

src/
├── actions
│ └── cart-actions.js
├── index.js
├── reducers
│ ├── cart-reducer.js
│ ├── index.js
│ └── products-reducer.js
└── store.js
複製代碼

而後,咱們把index.js中的代碼進行整理:

// src/actions/cart-actions.js

export const ADD_TO_CART = 'ADD_TO_CART';

export function addToCart(product, quantity, unitCost) {
  return {
    type: ADD_TO_CART,
    payload: { product, quantity, unitCost }
  }
}
複製代碼
// src/reducers/products-reducer.js

export default function(state=[], action) {
  return state;
}
複製代碼
// src/reducers/cart-reducer.js

import  { ADD_TO_CART }  from '../actions/cart-actions';

const initialState = {
  cart: [
    {
      product: 'bread 700g',
      quantity: 2,
      unitCost: 90
    },
    {
      product: 'milk 500ml',
      quantity: 1,
      unitCost: 47
    }
  ]
}

export default function(state=initialState, action) {
  switch (action.type) {
    case ADD_TO_CART: {
      return {
        ...state,
        cart: [...state.cart, action.payload]
      }
    }

    default:
      return state;
  }
}
複製代碼
// src/reducers/index.js

import { combineReducers } from 'redux';
import productsReducer from './products-reducer';
import cartReducer from './cart-reducer';

const allReducers = {
  products: productsReducer,
  shoppingCart: cartReducer
}

const rootReducer = combineReducers(allReducers);

export default rootReducer;
複製代碼
// src/store.js

import { createStore } from "redux";
import rootReducer from './reducers';

let store = createStore(rootReducer);

export default store;
複製代碼
// src/index.js

import store from './store.js';
import { addToCart }  from './actions/cart-actions';

console.log("initial state: ", store.getState());

let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
);

store.dispatch(addToCart('Coffee 500gm', 1, 250));
store.dispatch(addToCart('Flour 1kg', 2, 110));
store.dispatch(addToCart('Juice 2L', 1, 250));

unsubscribe();
複製代碼

整理完代碼以後,程序依然會正常運行。如今咱們來添加修改和刪除購物車中商品的邏輯。修改cart-actions.js和cart-reducer.js文件:

// src/reducers/cart-actions.jsexport const UPDATE_CART = 'UPDATE_CART';
export const DELETE_FROM_CART = 'DELETE_FROM_CART';
…
export function updateCart(product, quantity, unitCost) {
  return {
    type: UPDATE_CART,
    payload: {
      product,
      quantity,
      unitCost
    }
  }
}

export function deleteFromCart(product) {
  return {
    type: DELETE_FROM_CART,
    payload: {
      product
    }
  }
}
複製代碼
// src/reducers/cart-reducer.jsexport default function(state=initialState, action) {
  switch (action.type) {
    case ADD_TO_CART: {
      return {
        ...state,
        cart: [...state.cart, action.payload]
      }
    }

    case UPDATE_CART: {
      return {
        ...state,
        cart: state.cart.map(item => item.product === action.payload.product ? action.payload : item)
      }
    }

    case DELETE_FROM_CART: {
      return {
        ...state,
        cart: state.cart.filter(item => item.product !== action.payload.product)
      }
    }

    default:
      return state;
  }
}
複製代碼

最後,咱們在index.js中分發這兩個action:

// src/index.js// Update Cart
store.dispatch(updateCart('Flour 1kg', 5, 110));

// Delete from Cart
store.dispatch(deleteFromCart('Coffee 500gm'));
…
複製代碼
6.Redux在項目開發中使用:

​ 用戶發出 Action,Reducer 函數算出新的 State,View 從新渲染。可是,一個關鍵問題沒有解決:異步操做怎麼辦?

Action 發出之後,Reducer 當即算出 State,這叫作同步

可是在實際開發中action大部分的狀況是調用接口發送異步請求,也就是說:

Action 發出之後,過一段時間再執行 Reducer,這就是異步

redux-thunk 是一個比較流行的 redux 異步 action 中間件

1.導入thunk: import thunk from 'redux-thunk'。

2.導入中間件: import {createStore,applyMiddleware} from 'redux'。

3.建立store:let store = createStore(reducer函數,applyMiddleware(thunk))。

4.激活redux-thunk中間件,只須要在createStore中加入applyMiddleware(thunk)就能夠。

5.建立action 建立函數,利用redux-thunk 幫助你統一了異步和同步 action 的調用方式(把異步過程放在 action 級別解決)。

相關使用示例:

//1.相關組件的引入
import { createStore ,applyMiddleware} from 'redux'; 
import thunk from 'redux-thunk'; 

//2.新建store怎麼加入中間件
const store = createStore(counter,applyMiddleware(thunk));
 
//3.action函數怎麼使用 
export  function addGunAsync(){
	//thunk插件的做用,這裏能夠返回函數
	return dispatch =>{
		//異步結束後,手動執行dispatch
		setTimeout(() => {
            // addGUN()時你的action
		  	dispatch(addGUN())
		}, 2000)
	}
}
複製代碼

關於mapStateToProps 、mapDispatchToProps 和connect的用法能夠參考 www.cnblogs.com/hanmeimei/p…

借鑑相關文章: blog.csdn.net/xiangzhihon… blog.csdn.net/qq_42606051…

相關文章
相關標籤/搜索