《redux实战》第2章:第一个redux应用程序

279 阅读6分钟

本章学习内容已规整到对应专栏

1、使用cra创建一个任务管理应用程序

cra(create-react-app的简写),是Facebook于2016年年中发布的一个官方脚手架,这个工具能够帮你完成复杂的配置工作,如webpack、babel、ESLint等等一系列的工具。

// 创建新项目:
npx create-react-app my-redux

// 进入项目文件夹
cd my-redux

// 启动项目
npm start

2、创建基本的react组件

按照“由内而外”的开发习惯,先开发UI,再开发对应的行为功能。在上面创建的my-redux项目的src目录下,创建components文件夹,并在其中创建Task.js、TaskList.js、TasksPage.js。(具体的代码,请直接打开本页面尾部代码仓库查看)

3、配置redux store

1)整体和store api

redux中的主要核心功能是store,它是一个负责管理程序状态的对象,redux store也称为状态树。redux包会导出createStore函数,用来创建store。然后,redux store是一个提供了几个核心方法的对象,这些方法可以用于创建、读取、更新状态并响应状态的改变:、getState、dispatch和subscribe,可以通过下方的代码简单了解这三个方法的使用:

import { createStore } from 'redux'

// store至少需要一个reducer函数
const counterReducer = (state = 0, action) => {
    if(action.type === 'INCREMENT'){
        return state + 1
    }
    
    return state
}

// 使用reducer创建函数
const store = createStore(counterReducer)

// 读取store的当前状态
console.log('current state:', store.getState())

// 在store更新后做一些事,在将redux连接到react之后,这个方法会在底层调用,它能让react组件在store中的任一状态发生变化时进行重新渲染
store.subscribe(() => {
    console.log('current state:', store.getState())
})

// 向reducer发送一个新的action来更新store
store.dispatch({ type: 'INCREMENT' })

2)创建redux store

store包含一个或多个reducer以及可选的中间件,注意:创建store至少需要一个reducer。 image.png 首先,一定要在项目中添加redux:

npm install redux

(剩下的其他关于redux操作的具体代码,请直接打开本页面尾部代码仓库查看)

此处,扩展下关于数据结构的不可变性:

书中强烈建议保持数据的不可变性,也就是说,不要直接改变值。不可变性具有一些固有的有点,比如易于使用和测试,在使用redux的情况下,真正的优点是能够进行极其快速简单的相等性检查。

例如:如果在reducer中改变一个对象,react-redux的connect可能无法正确更新对应的组件,当connect比较旧状态和新状态以决定是否需要重新渲染时,只检查两个对象是否相等,而不检查对象里每个单独的属性是否相等。

总之,永远不要在redux中改变数据,reducer应该总是接收当前状态作为输入并计算全新的状态。

4、使用react-redux连接redux和react

如果要连接redux和react,就需要使用react-redux包中的react绑定,redux只提供了配置store的方法,而react-redux则提供了增强组件的能力,允许组件读取store的状态或者派发action,从而填补了react和redux的鸿沟。作为桥梁,react-redux提供了两个主要的工具用于将react store连接到react:

1)Provider组件

Provider是一个react程序顶层渲染的组件,它接收store作为属性,并在程序中包裹顶层组件,在Provider中渲染的任何子组件,无论嵌套多深,都有权限访问redux store

Provider组件可以视为启动器,一般不会经常直接与它交互,通常只会在负责将程序初始装载到DOM的文件中时用到(如:src/index.js)。而在背后,Provider可以确保使用connect将数据从store传递到一个或多个react组件

2)connect组件

一个用于将react组件和redux store数据桥接的函数。 connect函数的作用.png

3)关于容器组件和展示组件

将组件分成容器组件和展示组件是一种约定,而不是react和redux的硬性规定。

  • 展示型组件,不依赖于redux,它们不知道也不关心是否正在使用redux来管理应用程序状态,给定相同的数据,将始终获得相同的渲染输出;
  • 容器组件,展示性组件所需要的数据就是由容器组件来传递,容器组件负责通过connect从redux store获取数据,然后是用mapToPropState来只传递相关的数据到连接的组件,最后渲染展示型组件。另外,处理action也是在容器组件中进行。

所以,使用了connect函数之后,在处理redux的数据时,只需要关注容器组件,而不必担心用于展示的标记。

5、派发action

派发action,需要使用react-redux中的dispatch方法。什么是dispatch?store对其数据提供极强的保护,只提供了一种更新状态的方法——派发action,,dispatch是store API的一部分,connect通常将它作为属性提供给组件。 一个action将包含下面两个属性:

  • type,一个表示正在执行的action类别字符串。按照惯例,使用大写,并用下划线作为分隔符。这是一个有效action所需的唯一属性;
  • payload,为action提供所需数据的对象。payload字段是可选的,如果不需要额外的数据来执行action,可以省略。例如,用户退出的操作可能只包含LOGOUT这个类型字段而不必附加数据。但是,如果需要附加数据,那么任何键值都可以被传入action。在redux中,并没有要求使用payload这个名称,但这是一种流程的约定。

6、action创建器

在实际的开发中,一般不会直接在dispatch中派发action对象,而是通过调用action创建器返回的action对象来dispatch。通过下图可以明显知道这种关系: action创建器.png

action和action创建器是紧密相关的,它们合作将action派发到store,但履行不同的职责:

  • action,描述事件的对象;
  • action创建器,返回action的函数。

为什么要使用action创建器?因为action创建器有更友好的接口,只需要知道action创建器期望的参数即可,不必关心细节,比如action所携带数据的结构,或者在action被派发之前可能需要处理的任何逻辑。同样,action创建器的参数要求也能提供帮助,它们清楚地描述了action对数据的要求。

关于 副作用 的定义:副作用是任何对外部世界有显著影响的代码,例如写入磁盘或更改数据。换言之,这些代码做了除了接收输入和返回结果之外的事情。

7、使用reducer处理action

reducer的要点就是处理action,它接收store的当前状态和action,并在应用相关更新之后返回下一个状态。通过下图可以知道reducer的处理流程:

graph TD
action & 当前状态 --> reducer --> 下一个状态

8、对应代码

关于本章节的所有相关代码,请看代码仓库的chapter_two