- 像父子組件之間相互傳值相互調用的狀況,而且值的適用範圍僅限於父子組件之間,這時不須要使用Redux.
- 當某個子組件去更新某種狀態時,好比更新組織機構數據。而其餘的頁面又須要依賴這些數據時,此時能夠考慮使用redux,把這些狀態值放入到redux中進行管理。
Redux是針對JavaScript應用的可預測狀態容器 。javascript
可預測性(predictable)html
狀態容器(state container)java
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的總結。數組
配置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);
複製代碼
上面代碼的意思是:
- 從redux包中引入createStore()方法;
- 建立了一個名爲reducer的方法,第一個參數state是當前保存在store中的數據,第二個參數action是一個容器,用於: type - 一個簡單的字符串常量,例如ADD, UPDATE, DELETE等。 payload - 用於更新狀態的數據。
- 建立一個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.js
…
const 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.js
…
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;
}
}
…
複製代碼
咱們繼續來分析一下代碼。一個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.js
…
export 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.js
…
export 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'));
…
複製代碼
用戶發出 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…