美文网首页react
简单易懂的Redux

简单易懂的Redux

作者: 凉风hi | 来源:发表于2017-06-21 23:25 被阅读0次

简单易懂的Redux

根据redux官方文档来说,可以将redux分为三个核心部分。

  • actions
  • reducers
  • store

1. actions

其中,actions主要负责定义javascript object,这个js类是用在application和store之间传递数据的。简单的说,就是把一个操作抽象成一个类。举个例子,在web页面上的一个button,用户点击button,会触发一个click事件。这个click事件,我们可以抽象成一个actions类。我们给它一个type属性,来唯一定义这个事件,比如说,就叫buttonclick。那么这个actions就定义好了。

{
    type: 'buttonclick',
}

这个js类就是我们的单击button的actions。
对于其他的actions,例如text的改变也可以是一个actions,会包括text的值,那么就会变成下面的样子:

{
    type: 'textchange',
    textvalue: 'mytextvalue',
}

类似这样,就是抽象出来的两个类。但是,这样的actions,在textvalue改变的时候,是需要再次创建的,也就是说里面的属性一变就要创建一个新的actions。所以单纯的actions就没法满足这一点。就有了Actions creator。

Actions creator

Actions creator 就是创建actions的function函数。主要作用就是返回一个actions。一般情况下,如下:

export function textChange(textvalue) {
    return {
        type: 'textchange',
        textvalue,
    }
}

这样就是一个actions creator,用来创建上面的text actions,可以复用,比如说:

textChange('hello');

就创建了一个{type: 'textchange', textvalue: 'hello'}的actions。Actions creator可以在各种地方创建类似的actions。然后通过dispatch调用actions。dipatch的作用之后会讲。

2. reducers

我们现在定义好了每个操作的actions,把每个操作都抽象成了一个类。还少了几个部分:

  • actions如何和前端的views或者其他application相关绑定起来,来表明这个操作就是对应这个actions
  • 以及触发了这个actions会导致数据发生什么变化

我们先把数据变化的部分也抽象出来讲解,中间的数据传递和绑定之后在介绍。
触发actions导致的数据变化可以简写为:

原来的状态(state) + action => 新状态(new state)

reducer就是要定义这一过程。因此reducer一般都是这样的:

reducer(state,action){ return newState;}

所以我们要定义一个reducer,来通过点击按钮+1,那么这个数据变化就也可以实现成:

export function addReducers(state=0, action) {
    switch(action.type) {
        case 'buttonclick':
            return state + 1;
        default:
            return state;
    }
}

这样就定义好了一个reducer,它表明,当action类型是buttonclick的时候,state就会加1。我们可以用store.dispatch去使用这个reducer更新我们的state值。

因此,reducer的作用就是用来指明应用如何更新state。

3. store 和 store.dispatch

根据1,我们只定义了actions,但是这些actions只是我们抽象出来的一些js类,就和其他代码一样,抽象了一个类出来,却没有把它和我们的页面连起来,页面不知道哪个actions对应了什么操作,点击按钮也不会真的触发这个actions。
因此我们需要把这些actions和我们的页面组件连起来。
比如说,在我们的button上添加一个onClick属性,使它调用 dispatch函数,来触发这个action。大概是下面这样。

<Button onClick={store.dispatch({type: 'buttonclick'})/>

也可以通过之前定义的textChange这个action creator来创建action,并使用store.dispatch调用reducer。

<Text onChange={store.dispatch(textChange('hello')) ... />
// is same as
<Text onChange={store.dispatch({type: 'textchange', textvalue: 'hello'}) />

那么问题又来了,这里面有一个storestore.dispatch都是什么。
我们先介绍一下store,store就是连接actions和reducers的。actions指定了我们做了什么操作,reducers说明了我们做了这些操作之后数据发生了什么变化。而store则用来把它们连在一起。
简单来说,store提供了以下功能:

• 维持应用的 state;(英文原文是Holds application state;个人理解吧,就是store中有一个state属性,可以通过getState()方法获取。)
• 提供 getState() 方法获取 state;
• 提供 dispatch(action) 方法更新 state;
• 通过 subscribe(listener) 注册监听器;
• 通过 subscribe(listener) 返回的函数注销监听器。

这个就要涉及到redux的数据流了。简单来说,当用户对页面进行操作时就会有相关的事件,我们将actions绑定在这些事件上,然后这些事件触发的时候,就可以通过store.dispatch来调用,然后就会进行下面的操作,这里是redux中createStore的部分源码。简单浏览一下。

export default function createStore(reducer, preloadedState, enhancer) {
  // 这里的enhancer参数主要是一些第三方的功能,如midderware, time travel, persistence,可以为undefined
  ...
  let currentReducer = reducer
  let currentState = preloadedState // 初始化currentState, 可以为undefined
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false
  ...
  function getState() {
    return currentState // getState()返回的是当前的state值
  }
  ...
  function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
        'Use custom middleware for async actions.'
      )
    } // 判定dispatch的action是不是纯函数,不是纯函数报错

    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
        'Have you misspelled a constant?'
      )
    } // 判定action的type是否存在

    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    } // 这个isDispatching会在store创建的时候定义为false

    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    } // currentState只能通过currentReducer更新,调用了这个reducer,返回新的state值,用来更新currentState

    const listeners = currentListeners = nextListeners
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }
  ...

通过上面的代码,发现currentState只能通过currentReducer来更新状态。并且通过dispatch(action)这个操作,会执行currentState = currentReducer(currentState, action)这个就是更新了state。所以,当我们想要用store的时候,先创建一个store。也就是

const store = createStore(reducers)

然后这个store就可以被使用了。
先在index.js中注明这个store,类似下面这样,就是把store传给了App这个component,在App中就可以使用store了。也可以使用store的两个函数getState()和dispatch()。

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

因此,如果我们的上面的button和text是在App这个Component中定义的,那么现在上面的

<Button onClick={store.dispatch({type: 'buttonclick'})/>

就有效了,这个store.dispatch就会去改变state的状态。

这就是三者的关系了。

简单的数据流

用户相关操作 ---> 抽象成actions
用户操作导致的state变化 ---> 抽象成reducers
state + action ---> newState
store.dispatch主要实现了: state + action ---> newState

因此简单的数据流可以在redux的官方文档里面看到: [redux-flows]
这里不太想做过多介绍,因为上面都讲过了。

Redux 应用中数据的生命周期遵循下面 4 个步骤:

  1. 调用 store.dispatch(action) 。
  2. Redux store 调用传入的 reducer 函数。
  3. rootReducer 应该把多个子 reducer 输出合并成一个单一的 state 树。
  4. Redux store 保存了rootReducer 返回的完整 state 树。

这些其实就是createStore中的一部分,建议可以直接去redux-createStore.js读一下,代码挺简单的。之前也介绍过。dispatch就是调用了reducer而已。currentReducer是reducers的合集。
一般可以通过combineReducers函数组合起来。有空可以讲一下原理。然后store里面就是全部的state了。

本文需要结合redux官方文档看,感觉redux的中文文档有些翻译的不是特别好,有条件还是去看下英文文档,不过讲的很潦草。实践才能知道。

redux数据流

附录:
redux官方文档:中文:http://www.redux.org.cn/
英文: http://redux.js.org/
redux源代码:https://github.com/reactjs/redux

相关文章

网友评论

    本文标题:简单易懂的Redux

    本文链接:https://www.haomeiwen.com/subject/tokxcxtx.html