react数据流知识点整理(flux、redux、react-redux)

234 阅读5分钟

Flux架构模式

  1. Flux的核心思想: 数据和逻辑永远单向流动 Flux数据流.png

Action Creator

负责创建action,作为全部改变和交互的入口。当需要改变应用的状态或有View需要更新时,就需要发出一个action。产生的action就是一个普通对象,带有一个type属性。 action创建好以后,就会传递给Dispatcher。

Dispatcher

Dispatcher就是一个回调函数登记表,保存着有需要发送action的store列表。当action传过来,就会通过查表把这个action传递给所有注册的store。

Store

store保存着整个程序的state,而且更改逻辑都在store里。store存在多个,每一个会使用switch语句来判断action的类型,决定是否对这个action做出响应。如果store关心这个action,就会根据action找出需要变化的部分,更新state。更新state后,会触发一个onchange事件,Controller View会订阅这个事件。

Controller View

Controller View控制着所有的view,并且订阅Store中state的状态,当store中的状态更新完成,就会将view渲染成最新的state,用户看到的页面也就变化了。 另一方面,用户操作页面改变了view,Controller View会给Action Creator发起通知准备action。

因为flux存在很多不足之处,所以就不做代码演示,具体可以查看Andrew Ray 的文章《Flux For Stupid People》。 redux就是基于flux思想与函数式编程结合的热门架构。

Redux

Redux使用场景

  • 用户的使用方式复杂
  • 不同身份的用户有不同的使用方式(比如普通用户和管理员)
  • 多个用户之间可以协作
  • 与服务器大量交互,或者使用了WebSocket
  • View要从多个来源获取数据

"如果你不知道是否需要 Redux,那就是不需要它。只有遇到 React 实在解决不了的问题,你才需要 Redux 。"

Redux流程图.png

Action

Action是把数据从应用传到store的载体,一定要有一个type属性。Action本质上是一个个js普通对象,可以定义一个函数来生成Action。

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

Reducer

整个应用只有一个单一的reducer函数,这个函数就是传给createStore的第一个参数。reducer就是一个类似 (previousState, action) => newState 特征的纯函数。 项目中应该按照更新逻辑把reducer拆分成多个,每个子reducer负责独立管理state的一部分。

const initialState = {visibilityFilter: filter};
const todos = (state=initialState, action) => {
   switch (action.type) {
       case SET_VISIBILITY_FILTER:
         return Object.assign({}, state, {
           visibilityFilter: action.filter
         })
       case ADD_TODO:
           return { ...state, visibilityFilter: action.filter }
       default:
         return state
   }
};

Redux 提供了一个combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。

import { combineReducers } from 'redux';

const reducer = combineReducers({
  todos,
  counter
})

Store

Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。

1. 创建Store

import { createStore } from 'redux';
import reducer from './reducer';

const store = createStore(reducer, window.STATE_FROM_SERVER, applyMiddleware(thunk))

store.dispatch方法会触发 Reducer 的自动执行。为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore方法。中间件 applyMiddleware

createStore()的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。

2.发起action

import { addTodo } from './action';
import { reducer } from './reducer';

const store = createStore(reducer);
const {dispatch, getState, subscribe} = store;
dispatch(addTodo('Learn Redux'))

3.注册监听器

Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。

import { createStore } from 'redux';
const store = createStore(reducer);

store.subscribe(listener);

显然,只要把 View 的更新函数(对于 React 项目,就是组件的render方法或setState方法)放入listen,就会实现 View 的自动渲染。 store.subscribe方法返回一个函数,调用这个函数就可以解除监听。


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

unsubscribe();

React-Redux

为了方便使用,Redux的作者封装了一个React专用的库React-Redux

UI组件

React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。

UI 组件有以下几个特征。

  • 只负责 UI 的呈现,不带有任何业务逻辑
  • 没有状态(即不使用this.state这个变量)
  • 所有数据都由参数(this.props)提供
  • 不使用任何 Redux 的 API

下面就是一个 UI 组件的例子。


const Title =
  value => <h1>{value}</h1>;

因为不含有状态,UI 组件又称为"纯组件",即它纯函数一样,纯粹由参数决定它的值。

容器组件

  • 负责管理数据和业务逻辑,不负责 UI 的呈现
  • 带有内部状态
  • 使用 Redux 的 API

React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。

import { connect } from 'react-redux'

const mapStateToProps = (state, ownProps) => {
  return {
    active: ownProps.filter === state.visibilityFilter
  }
}

const mapDispatchToProps = (
  dispatch,
  ownProps
) => {
  return {
    onClick: () => {
      dispatch({
        type: 'SET_VISIBILITY_FILTER',
        filter: ownProps.filter
      });
    }
  };
}

const VisibleTodoList = connect(mapStateToProps, mapDispatchToProps)(TodoList);
  • TodoList就是UI组件,VisibleTodoList就是容器组件
  • mapStateToProps:将store中的数据作为props绑定到组件上
  • mapDispatchToProps: 定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象

Provider组件

connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。

一种解决方法是将state对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state传下去就很麻烦。

React-Redux 提供Provider组件,可以让容器组件拿到state

  • 它的原理是React组件的context属性
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'

let store = createStore(todoApp);

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