redux入门_Redux入门

redux入门

典型的Web应用程序通常由几个共享数据的UI组件组成。 通常,多个组件的任务是显示同一对象的不同属性。 该对象表示可以随时更改的状态。 在多个组件之间保持状态一致可能是一场噩梦,尤其是当有多个通道用于更新同一对象时。

以一个有购物车的网站为例。 在顶部,我们有一个UI组件,显示购物车中的物品数量。 我们还可以使用另一个UI组件来显示购物车中商品的总费用。 如果用户单击“ 添加到购物车”按钮,则这两个组件均应立即以正确的数字进行更新。 如果用户决定从购物车中删除物品,更改数量,添加保护计划,使用优惠券或更改运输位置,则相关的UI组件应更新以显示正确的信息。 如您所见,一个简单的购物车会随着其功能范围的扩大而Swift变得难以保持同步

在本指南中,我将向您介绍一个称为Redux的框架,该框架可以帮助您以易于扩展和维护的方式构建复杂的项目。 为了简化学习,我们将使用一个简化的购物车项目来了解Redux的工作方式。 您至少需要熟悉React库,因为稍后需要将其与Redux集成。

先决条件

在开始之前,请确保您熟悉以下主题:

另外,请确保在计算机上进行以下设置:

您可以在GitHub上访问本教程中使用的全部代码。

什么是Redux

Redux徽标 Redux是一种流行JavaScript框架,为应用程序提供了可预测的状态容器。 Redux基于Flux(Facebook开发的框架)的简化版本。 与标准MVC框架不同,在标准MVC框架中,数据可以在UI组件和存储之间双向流动,而Redux严格允许数据仅在一个方向流动。 请参见下图:

Redux流程图

图1:Redux流程图

在Redux中,所有数据(即状态 )都保存在称为store的容器中。 一个应用程序中只能有一个。 该存储实质上是一棵状态树,其中保留了所有对象的状态。 任何UI组件都可以直接从商店访问特定对象的状态。 要从本地或远程组件更改状态,需要调度一个动作 。 在这种情况下, 调度意味着向商店发送可操作的信息。 商店收到action ,会将其委托给相关的reducer 。 约简reducer只是一个纯函数,它查看先前的状态,执行操作并返回新的状态。 为了看到所有这些,我们需要开始编码。

首先了解不变性

在开始之前,我需要您首先了解不变性在JavaScript中的含义。 根据牛津英语词典,不变性意味着不可改变 。 在编程中,我们编写的代码始终会更改变量的值。 这称为可变性 。 我们这样做的方式通常会在我们的项目中导致意外的错误。 如果您的代码仅处理原始数据类型(数字,字符串,布尔值),则无需担心。 但是,如果您使用数组和对象,则对它们执行可变操作可能会导致意外错误。 为了演示这一点,请打开您的终端并启动Node交互式shell:

node

接下来,让我们创建一个数组,然后将其分配给另一个变量:

> let a = [1,2,3]
> let b = a
> b.push(9)
> console.log(b)
[ 1, 2, 3, 9 ] // b output
> console.log(a)
[ 1, 2, 3, 9 ] // a output

如您所见,更新array b也会导致array a也发生变化。 发生这种情况是因为对象和数组是已知的引用数据类型 -意味着此类数据类型本身实际上并不保存值,而是指向存储值的内存位置的指针。 通过将a分配给b ,我们仅创建了引用相同位置的第二个指针。 要解决此问题,我们需要将引用的值复制到新位置。 在JavaScript中,有三种不同的方法可以实现此目的:

  1. 使用Immutable.js创建的不可变数据结构
  2. 使用JavaScript库,如下划线Lodash执行一成不变的操作
  3. 使用本机ES6函数执行不变的操作。

在本文中,我们将使用ES6方式,因为它已经在NodeJS环境中可用。 在您的NodeJS终端中,执行以下操作:

> a = [1,2,3] // reset a
[ 1, 2, 3 ]
> b = Object.assign([],a) // copy array a to b
[ 1, 2, 3 ]
> b.push(8)
> console.log(b)
[ 1, 2, 3, 8 ] // b output
> console.log(a)
[ 1, 2, 3 ] // a output

在上面的代码示例中,现在可以修改数组b而不影响数组a 。 我们已经使用Object.assign()创建了变量b现在指向的值的新副本。 我们还可以使用rest operator(...)来执行如下不变操作:

> a = [1,2,3]
[ 1, 2, 3 ]
> b = [...a, 4, 5, 6]
[ 1, 2, 3, 4, 5, 6 ]
> a
[ 1, 2, 3 ]

其余运算符也可以处理对象文字! 我不会深入探讨这个主题,但是这里有一些我们将用来执行不可变操作的ES6其他功能:

如果我链接的文档没有用,请不用担心,因为您将看到它们在实际中的用法。 让我们开始编码!

设置Redux

设置Redux开发环境的最快方法是使用create-react-app工具。 在开始之前,请确保您已经安装并更新了nodejsnpmyarn 。 让我们通过生成redux-shopping-cart项目并安装Redux软件包来设置Redux项目:

create-react-app redux-shopping-cart

cd redux-shopping-cart
yarn add redux # or npm install redux

删除src文件夹中除index.js之外的所有文件。 打开文件并清除所有现有代码。 输入以下内容:

import { createStore } from "redux";

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

const store = createStore(reducer);

让我解释一下以上代码的作用:

  • 第一个陈述 。 我们从Redux包中导入了createStore()函数。
  • 第二条陈述 。 我们创建一个称为reducer的空函数。 第一个参数state是存储在存储中的当前数据。 第二个参数action是一个容器,用于:
    • 类型 —一个简单的字符串常量,例如ADDUPDATEDELETE等。
    • 有效负载 —用于更新状态的数据
  • 第三条陈述 。 我们创建一个Redux存储,只能使用reducer作为参数来构建它。 可以直接访问Redux存储中保存的数据,但只能通过提供的reducer进行更新。

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

您可能已经注意到我提到了当前数据,就好像它已经存在一样。 当前,我们的state是undefined或null。 为了解决这个问题,只需为默认状态分配一个默认值,使其成为一个空数组即可:

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

现在,让我们开始实践。 我们创建的减速器是通用的。 它的名称并未说明其用途。 然后是我们如何使用多个异径管的问题。 答案是使用Redux软件包提供的combineReducers函数。 如下更新代码:

// src/index.js
…
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重命名为cartReducer 。 我还创建了一个名为productsReducer的新的空减速器,以向您展示如何使用combineReducers函数在单个商店中组合多个减速器。

接下来,我们将研究如何为减速器定义一些测试数据。 如下更新代码:

// 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());

只是为了确认商店有一些初始数据,我们使用store.getState()在控制台中打印出当前状态。 您可以通过在控制台中执行npm startyarn start来运行开发服务器。 然后按Ctrl+Shift+I在Chrome中打开检查器标签,以查看控制台标签。

Redux初始状态

图2:Redux初始状态

目前,我们的cartReducer什么也不做,但是应该管理Redux商店中我们购物车项目的状态。 我们需要定义用于添加,更新和删除购物车项目的操作。 让我们从定义ADD_TO_CART动作的逻辑开始:

// 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;
  }
}
…

花时间分析和理解代码。 减速器应该处理不同的操作类型,因此需要SWITCH语句。 当将类型ADD_TO_CART操作分派到应用程序中的任何位置时,此处定义的代码将对其进行处理。 如您所见,我们正在使用action.payload提供的信息来合并到现有状态,以创建新状态。

接下来,我们将定义一个action ,它是store.dispatch()的参数所store.dispatch()动作只是必须具有type和可选有效负载JavaScript对象。 让我们继续在cartReducer函数之后定义一个:

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

在这里,我们定义了一个返回纯JavaScript对象的函数。 没有什么花哨。 在分派之前,让我们添加一些代码,使我们能够侦听存储事件的更改。 将此代码放在console.log()语句之后:

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

unsubscribe();

接下来,让我们通过向商店分派动作来将几个商品添加到购物车中。 将此代码unsubscribe()之前:

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

为了清楚起见,我将在下面说明进行上述所有更改后如何显示整个代码:

// 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应该会自动刷新。 检查控制台选项卡以确认已添加新项目:

调度Redux动作

图3:分派的Redux操作

组织Redux代码

index.js文件已Swift变大。 这不是Redux代码的编写方式。 我只是为了向您展示Redux是多么简单。 让我们看看应该如何组织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-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
    }
  }
}

接下来,如下更新cart-reducer.js

// 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调度UPDATE_CARTDELETE_FROM_CART操作:

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

// Delete from Cart
store.dispatch(deleteFromCart('Coffee 500gm'));
…

保存所有更改后,浏览器应自动刷新。 检查控制台选项卡以确认结果:

Redux更新和删除操作

图4:Redux更新和删除操作

如已确认的那样,将1公斤面粉的数量从2更新为5,同时将500克咖啡从购物车中删除。

使用Redux工具进行调试

现在,如果我们在代码中犯了一个错误,我们如何调试Redux项目?

Redux附带了许多第三方调试工具,我们可以使用它们来分析代码行为并修复错误。 可能最受欢迎的工具时间旅行工具 ,也称为redux-devtools-extension 。 设置过程分为三个步骤。 首先,转到您的Chrome浏览器并安装Redux Devtools扩展程序

Redux DevTools Chrome扩展

图5:Redux DevTools Chrome扩展

接下来,转到运行Redux应用程序的终端,然后按Ctrl+C停止开发服务器。 接下来,使用npm或yarn安装redux-devtools-extension软件包。 就个人而言,我更喜欢Yarn,因为有一个yarn.lock文件,我想保持更新。

yarn add redux-devtools-extension

安装完成后,您可以在我们实施工具的最后一步时启动开发服务器。 打开store.js并替换现有代码,如下所示:

// src/store.js
import { createStore } from "redux";
import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer from './reducers';

const store = createStore(rootReducer, composeWithDevTools());

export default store;

随时更新src/index.js并删除所有与登录到控制台并订阅商店有关的代码。 不再需要。 现在,返回Chrome并通过右键单击工具图标打开Redux DevTools面板:

Redux DevTools菜单

图6:Redux DevTools菜单

就我而言,我选择了“到底部”选项。 随意尝试其他选择。

Redux DevTools面板

图7:Redux DevTools面板

如您所见,Redux Devtool非常出色。 您可以在action,state和diff方法之间切换。 在左侧面板上选择操作,然后观察状态树的变化。 您也可以使用滑块播放操作序列。 您甚至可以直接从该工具调度! 请查阅文档,以了解更多有关如何进一步根据需要定制工具的信息。

与React集成

在本教程的开始,我提到Redux与React确实搭配得很好。 好吧,您只需要几个步骤即可设置集成。 首先,停止开发服务器,因为我们需要安装react-redux软件包,这是React的官方Redux绑定:

yarn add react-redux

接下来,更新index.js以包含一些React代码。 我们还将使用Provider类将React应用程序包装在Redux容器中:

// src/index.js
…
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';

const App = <h1>Redux Shopping Cart</h1>;

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

这样,我们已经完成了集成的第一部分。 现在,您可以启动服务器以查看结果。 第二部分涉及使用刚刚安装的react-redux软件包提供的功能将React的组件与Redux存储和动作链接。 另外,您需要使用ExpressFeathers之类的框架来设置API。 该API将使我们的应用程序可以访问数据库服务。

在Redux中,我们还需要安装其他软件包(例如axios以通过Redux操作执行API请求。 然后,我们的React组件状态将由Redux处理,确保所有组件都与数据库API同步。 要了解有关如何完成所有这些操作的更多信息,请查看我的其他教程“ 使用React,Redux和FeathersJS构建CRUD应用程序 ”。

摘要

希望本指南对Redux有所帮助。 但是,您还有很多东西需要学习。 例如,您需要学习如何处理异步操作,身份验证,日志记录,处理表单等。 既然您了解了Redux的全部含义,您将发现尝试其他类似的框架(例如Flux ,Alt.js或Mobx)更加容易 。 如果您认为Redux适合您,我强烈推荐以下教程,这些教程将帮助您获得有关Redux的更多经验:

翻译自: https://www.sitepoint.com/getting-started-redux/

redux入门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值