redux 学习笔记
redux
举个例子,一个带有购物车的网站。在顶部,我们用一个UI组件显示购物车中的商品数量,我们还可以用另一个UI组件,显示购物车中商品的总价。如果用户点击添加到购物车按钮,则这两个组件应该立即更新当前的数据,如果用户从购物车中删除商品,更改商品数目,使用优惠卷或者更改送货地点,则相关的UI组件都应该更新出正确得信息。可以看到,随着功能范围的扩大,一个简单的购物车将会很难保持数据同步。redux 以简单易用的方式构建复杂项目并进行维护。
在redux 中所有的数据(比如state)被保存在一个被成为store的容器中,在一个应用程序中只能由一个。store 本质上是一个状态树,保存了所有对象的状态,任何UI组件都是可以直接从store访问特定对象的状态。要通过本地或远程组件更改状态,需要分发一个action,分发在这里意味着奖执行信息发送到store,当一个store 接受到一个action ,他将把这个action代理给相关的reducer,reducer 是一个纯函数,他可以查看之前的状态,执行一个cation 并且返回一个新的状态。
-
打开一个终端并启动node
-
创建一个数组,并赋值给另一个变量
1 let a = [1,2,3] 2 let b = a 3 b.push(4) 4 a 5 // [1,2,3,4] 6 b 7 // [1,2,3,4]
可以看到,更新数组b也会同时改变数组a。这是因为对象和数组是引用数据类型,这意味着这样的数据类型实际上并不保存值,而是存储指向内存单元的指针。将a赋值给b,其实我们只是创建了第二个指向统一存储单元的指针。药解决这个问题,我们需要将引用的值复制到一个新的存储单元,再JavaScript中,有三种不同的实现方式。
-
使用Immutable.js创建不可变的数据结构。
-
-
使用ES6方法执行不可变操作。
1 let a = [1,2,3] 2 let b = Object.assign([], a) 3 let c = [...a, 4] 4 b.push(4) 5 a 6 // [1,2,3] 7 b 8 // [1,2,3,4] 9 c 10 // [1,2,3,4]
1 npx create-react-app my-app 2 cd my-app 3 yarn add redux 4 yarn start
首先,删除src 文件夹中除index.js 以外的所有文件。打开index.js, 删除代码,输入以下内容
1 import { createStore } from 'redux' 2 const reducer = function(state=[], action) { 3 return state 4 } 5 const store = createStore(reducer)
-
-
创建了一个名为reducer 的方法。第一个参数state是当前保存在store中的数据,第二个参数action 是一个容器,用于:
-
type: 一个简单的字符串常量,例如:ADD,DELETE,UPDATE
-
payload: 用于更新状态的数据。
-
-
创建一个Redux 存储区,它只能使用reducer作为参数来构造。存储在Redyx存储区中的数据可以直接被访问,但是只能通过提供的reducer进行更新。
让我们更进一步。目前我们创建的reducer是通用的。它的名字没有描述它的用途。那么我们如何使用多个reducer呢?我们将用到Redux包中提供的combineReducers函数。修改代码如下:
1 // src/index.js 2 3 import { createStore } from "redux"; 4 import { combineReducers } from 'redux'; 5 6 const productsReducer = function(state=[], action) { 7 return state; 8 } 9 10 const cartReducer = function(state=[], action) { 11 return state; 12 } 13 14 const allReducers = { 15 products: productsReducer, 16 shoppingCart: cartReducer 17 } 18 19 const rootReducer = combineReducers(allReducers); 20 21 let store = createStore(rootReducer);
整个index.js 文件
1 import {createStore, combineReducers} from 'redux' 2 3 // 初始化数据 4 const initialState = { 5 cart: [ 6 { 7 product: 'bread 700g', 8 quantity: 2, 9 unitCost: 90 10 }, 11 { 12 product: 'milk 700ml', 13 quantity: 2, 14 unitCost: 90 15 } 16 ] 17 } 18 19 const ADD_TO_CART = 'ADD_TO_CART' 20 21 const productsReducer = function(state = [], action){ 22 return state 23 } 24 const cartReducer = function(state=initialState, action) { 25 switch(action.type) { 26 case ADD_TO_CART: { 27 return { 28 ...state, 29 cart: [...state.cart, action.payload] 30 } 31 } 32 default: 33 return state 34 } 35 } 36 const allReducers = { 37 products: productsReducer, 38 shoppingCart: cartReducer 39 } 40 // 使用combineReducers 组合多个reducer 41 const rootReducer = combineReducers(allReducers) 42 // 创建store 实例 43 const store = createStore(rootReducer) 44 // 订阅监听每次数据变化 45 let unsubscribe = store.subscribe(()=>{ 46 console.log(store.getState()) 47 }) 48 49 function addToCart (product, quantity, unitCost) { 50 return { 51 type: ADD_TO_CART, 52 payload: { product, quantity, unitCost} 53 } 54 } 55 // 分发指令,改变状态 56 store.dispatch(addToCart('coffee 50gm', 1, 250)) 57 store.dispatch(addToCart('Flour 1kg', 2, 220)) 58 store.dispatch(addToCart('Juice 2L', 2, 110)) 59 60 unsubscribe(); 61 62 console.log('initial state: ' + store.getState())
1 // cart-actions.js 2 export const ADD_TO_CART = 'ADD_TO_CART' 3 export const UPDATE_CART = 'UPDATE_CART' 4 export const DELETE_FROM_CART = 'DELETE_FROM_CART' 5 6 export function addToCart(product, quantity, unitCost) { 7 return { 8 type: ADD_TO_CART, 9 payload: {product, quantity, unitCost} 10 } 11 } 12 13 export function updateCart(product, quantity, unitCost) { 14 return { 15 type: UPDATE_CART, 16 payload: {product, quantity, unitCost} 17 } 18 } 19 20 export function deleteFromCart(product) { 21 return { 22 type: DELETE_FROM_CART, 23 payload: {product} 24 } 25 }
1 // products-actions.js 2 export const ADD_TO_PRODUCT = 'ADD_TO_PRODUCT' 3 export const UPDATE_PRODUCT = 'UPDATE_PRODUCT' 4 export const DELETE_PRODUCT = 'DELETE_PRODUCT' 5 6 export function addToProduct(name, price, storageNum){ 7 return { 8 type: ADD_TO_PRODUCT, 9 payload: {name, price, storageNum} 10 } 11 } 12 13 export function updateProduct(name, price, storageNum) { 14 return { 15 type: UPDATE_PRODUCT, 16 payload: {name, price, storageNum} 17 } 18 } 19 20 export function deleteProduct(product) { 21 return { 22 type: DELETE_PRODUCT, 23 payload: {product} 24 } 25 }
1 // cart-reducer.js 2 import { ADD_TO_CART, UPDATE_CART, DELETE_FROM_CART } from '../actions/cart-actions' 3 4 const initialState = { 5 cart: [ 6 { 7 product: 'bread 700g', 8 quantity: 2, 9 unitCost: 90 10 }, 11 { 12 product: 'milk 500ml', 13 quantity: 1, 14 unitCost: 30 15 }, 16 { 17 product: 'coffee 300ml', 18 quantity: 2, 19 unitCost: 180 20 } 21 ] 22 } 23 24 export default function (state = initialState, action) { 25 switch (action.type) { 26 case ADD_TO_CART: { 27 return { 28 ...state, 29 cart: [...state.cart, action.payload] 30 } 31 } 32 case UPDATE_CART: { 33 return { 34 ...state, 35 cart: state.cart.map(item => item.product === action.payload.product ? action.payload: item) 36 } 37 } 38 case DELETE_FROM_CART: { 39 return { 40 ...state, 41 cart: state.cart.filter(item => item.product !== action.payload.product) 42 } 43 } 44 default: 45 return state 46 } 47 }
1 // products-reducer.js 2 import { ADD_TO_PRODUCT, UPDATE_PRODUCT, DELETE_PRODUCT } from '../actions/products-actions' 3 const initialState = { 4 products: [ 5 { 6 name: '冰激凌', 7 price: 35, 8 storageNum: 100, 9 }, 10 { 11 name: '锅巴', 12 price: 5, 13 storageNum: 100 14 } 15 ], 16 } 17 18 export default function (state = initialState, action) { 19 switch(action.type){ 20 case ADD_TO_PRODUCT: { 21 return { 22 ...state, 23 products: [...state.products, action.payload] 24 } 25 } 26 case UPDATE_PRODUCT: { 27 return { 28 ...state, 29 products: state.products.map(item => item.name === action.payload.name ? action.payload: item) 30 } 31 } 32 case DELETE_PRODUCT: { 33 return { 34 ...state, 35 products: state.products.filter(item => item.name !== action.payload.name) 36 } 37 } 38 default: { 39 return state 40 } 41 } 42 }
1 // reducers/index.js 2 import {combineReducers} from 'redux' 3 import productsReducer from './products-reducer' 4 import carReducer from './cart-reducer' 5 6 const allReducers = { 7 products: productsReducer, 8 shoppingCart: carReducer 9 } 10 11 const rootReducer = combineReducers(allReducers) 12 export default rootReducer
1 // src/store.js 2 import { createStore } from 'redux' 3 import rootReducer from './reducers' 4 5 let store = createStore(rootReducer) 6 export default store
1 // src/index.js 2 import store from './store.js' 3 import { addToCart, updateCart, deleteFromCart } from './actions/cart-actions' 4 import { addToProduct, updateProduct, deleteProduct } from './actions/products-actions' 5 console.log("initial state:", store.getState()) 6 7 // 订阅监听每次值变化 8 let unsubscribe = store.subscribe(() => { 9 console.log(store.getState()) 10 }) 11 12 // 新增 13 store.dispatch(addToCart('Flour 1kg', 2, 100)) 14 store.dispatch(addToCart('Juice 2L', 1, 200)) 15 // 修改 16 store.dispatch(updateCart('Flour 1kg', 5, 110)) 17 // 删除 18 store.dispatch(deleteFromCart('coffee 300ml')) 19 20 store.dispatch(addToProduct('辣条', 10, 100)) 21 store.dispatch(addToProduct('酸奶', 13, 150)) 22 store.dispatch(addToProduct('方便面', 10, 100)) 23 24 store.dispatch(updateProduct('辣条', 100, 6)) 25 store.dispatch(deleteProduct('方便面')) 26 27 store.dispatch(addToProduct('酸辣粉', 60, 8)) 28 unsubscribe()



浙公网安备 33010602011771号