Redux 整合笔记一 初探

508 阅读6分钟

一 redux 思想

react 解决了 MVC(Model-View-Controller)中的V -的最佳解决方案。 redux 解决了 如何管理达到一定规模的程序大小和复杂度。

redux通过一个简单的前提,一份巨大的回报,一次难忘的介绍,彻底征服了react世界。 这个前提是:使用纯函数将整合应用的状态存储在一个单独的对象中。 这份回报是:应用程序的状态将会是完全可预见的。 介绍来自:Dan Abramov 在2015年欧洲会议上的演讲

1.2 什么是Flux

为了理解redux,首先要介绍下Flux。

Flux是Chen为Facebook开发的一种架构模式。它试图解决状态的不可预知性,以及模型与试图紧密耦合的脆弱性。许多框架使用双向数据绑定two-way data bingding,但Chen担心杂乱的依赖网会产生意想不到后果,转而采用单向数据流。

Flux要求对状态的所有修改遵循单一的路径,而不允许每个view与对应的模型进行交互。

action

状态的每一次改变都始于一个action,action是描述应用程序事件的JS对象,它们通常由用户交互或服务器事件产生。

dispatcher

用来接收所有的action,并将它们发送到已注册的每个store。

store

每个store管理应用程序中的一个域的状态。一旦把一个store注册到dispatcher,它就开始接收action。当store接收到它关心的action时,它就会进行相应脱更新。一旦store产生更改,就会广播一个事件,让视图使用新的状态更新。

view

对view而言,它只需订阅要显示的数据的store。

1.3 什么是redux

从技术上讲,redux可能不符合Flux的实现。比如它移除了dispatcher。redux是类Flux的架构,区别在于语义的问题。

redux将状态管理职责划分如下独立单元

  • store 将应用程序所有状态都存在单个对象中
  • 只能使用action更新store, action是描述事件的对象
  • 被称作 reducer 的函数指定了如何转换应用程序的状态。reducer是它接收store中的当前状态和一个action,然后返回更新后的下一个状态。

React 和 Redux

使用react-redux包将react和redux绑定起来

Redux有助于定义应用程序做什么,而React将处理应用程序的外观。

  • 应用程序的状态有行为 - 由redux处理
  • 绑定 - 由react-redux包提供
  • 包含大部分view层的无状态组件

3个原则

单一数据源

使用单一的store具有重要的意义,可在单个对象中表示整个应用程序状态的能力,简化了开发人员的体验。

状态是只读的

action是应用程序状态发起改变的唯一方式。

改变由纯函数进行

通过reducer接收action,这些reducer是纯函数。纯函数是确定的,给定相同的输入总是产生同样的输出,且不会改变任何数据。否则可能会出现错误的新状态。

工作流

看看数据如何在react/redux中流动

action

{
	type: CREATE_POST,
    payload: {
    	body: 'All that is gold does not glitter'
    }
}
  • type - 表达正在执行的action类别的字符串。
  • payload - 提供执行action必要的数据的一个对象。paylpad(有效载荷)仅是惯例

action描述一个事件、它不知道也不关心下游会发生什么。最终、沿途的某处需要指定如何处理action。

reducer

reducer是负责更新状态以响应action的函数。接收当前状态和一个action作为参数,reducer的唯一职责是这些参数计算下一个状态并返回。

store

reducer描述了如何更新状态以响应action,但它们无法直接修改状态。这种特权仅限于store拥有。

store有以下九个主要角色

  • 持有应用程序状态
  • 提供一种访问状态的方式
  • 提供一种方式来指定对状态的更新。store要求派发一个action来修改状态
  • 允许其他实体订阅更新。通过react-redux提供的读图绑定可接收来自store的更新,并在组件中响应

reducer计算出下一个状态,然后轮到store更新自身,并将新状态广播给所有已注册的监听者。

二 第一个redux应用程序

2.1 配置redux store

createStore创建redux store getState() 获取state dispatch(action) 派发action subscribe(listener) 在react-redux底层使用

store.js

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

const store = createStore(rootReducer);

reducer.js

import { ADD_TODO } from "../actions/actionTypes";

const initialState = {
  filter: 'all',
  text: '',
  todos: []
}

const todoApp = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TODO:
      return {
        ...state,
        todos: [
          ...state.todos,
          {
            id: action.id,
            text: action.text,
            completed: false,
          },
        ],
      };
      break;
    // ...
    default:
      return state
      break;
  }
};

export default todoApp;
理解数据不可变性

强烈建议保持数据的不可变性,尽管这不是需严格遵循的要求。因为

  • 性能优化
  • 易于调试和跟踪
  • 易于推测

如果在reducer中改变一个对象,react-redux的connect可能无法正确更新对应的组件。 总之,永远不要在redux中改变数据。reducer应该总是接收当前状态作为输入并计算全新的状态。可使用ImmutableJS实现不可部数据结构。

2.2 使用react-redux连接react与redux

react-redux提供了两个主要的工具用于将Redux store 连接到react

  • Provider - 在react顶层渲染的React组件。Provider的任何子组件都有权限访问Redux store
  • connect - 一个用于将React组件与Redux store数据桥接的函数
import {Provider} from 'react-redux';
import store from './store';
const App = (
    <Provider store=store>  // provider内部组件都可获取store
        <TodoList />
    </Provider>
)

将数据从redux传递到react组件

connect 和 mapStateToProps通过向App组件中添加connect,可将其声明为Redux store中数据的入口点。

mapStateToProps返回的任何内容都将作为属性传递给组件。

import {connect} from 'react-redux';

class App extends Component {
	render(){
    	return (
        	<div> {this.props.tasks}</div>
        )
    }
}

const mapStateToProps = (state) => {
  return {
    tasks: state.tasks
  }
}

export default connect(mapStateToProps)(App);

容器组件和展示型组件

展示型组件不依赖于redux,它们不知道也不关心是否正在使用redux来管理应用程序状态。

容器组件需要从redux store中获取数据并传递给展示组件。

将组件分为容器组件和展示组件,这是十分流行的模式之一。这种模式将程序的表现和行为分开了。

2.7 派发action

dispatch是store API 的一部分,connect通常将它作为属性提供给组件。 action包含两个属性

  • type -- 一个表示正在执行的action类别的字符串。
  • payload -- 为action执行提供所需数据的对象。
this.props.dispatch({
	type: CREATE_TASK,
    payload: {
    	title,
        discription
    }
})

2.8 action创建器

上面我们直接dispatch了CREATE_TASK,但通常我们不会这样做,而是调用action创建器 - 返回action的函数

为什么要使用action创建器?action创建器有更友好的接口,只需要知道action创建器期望的参数即可,不必关心细节,比如action所携带数据的结构,或者在action被dispatch之前可能需要处理的任何逻辑。

src/actions/index.js

export createTask = ({title, description}) => {
 return {
 	type: CREATE_TASK,
    paylpad: {
    	id: uniqueId(),
        title,
        description,
        status: 'Unstarted'
    }
 }
}

src/app.js

import {createTask} from './actions';
...
onCreateTask = ({title, description}) => {
	this.props.dispatch(createTask({title, description}))
...

2.9 使用reducer 处理 action

store的作用是管理应用程序状态:它存储数据、控制访问,并且允许组件侦听更新。store不能也做不到的是定义响应动作时状态如何改变。这需要由你来决定,reducer正是实现这一机制的工具。

src/reducers/index.js

export default function tasks(state = {stask: mockTasks}. action) {
	switch (action.type) {
      case CREATE_TASK:
        return {
        	tasks: state.tasks.concat(action.payload)
        }
      // ...
      default:
        return state
        break;
    }
}