React-Redux学习小记

上一篇中较为系统的学习了一遍React-React学习小记
基本知道了React是什么,它跟jquery的区别。

利用React,可以利用组件式的思想进行前端代码的编写,而在组件(component)中,又可以按照组件的声明周期来进行较为精确的控制,在不同的声明周期中,可以嵌入其他例如jquery来进行定时任务以及ajsx请求等。同时,React以状态机以及虚拟DOM的思想去管理整个component。一旦state变化,会引起相应component的重新渲染。

下面进入正题:

Flux介绍

说Redux,不得不先介绍Flux,Flux是一种编程的思想,专门用于解决软件的架构问题,就像MVC思想解决web网站架构一样。
Flux将一个应用分为4个部分:

  • View: 视图层,用于显示的
  • Action(动作):视图层发出的消息(比如鼠标点击实践)
  • Dispatcher(派发器):用来接收Actions、执行回调函数,不同的Action,可以对应于不同的处理。
  • Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面

Flux最大的特点,就是单项流动,基本的流程为:

  • 1、用户访问 View
  • 2、View 发出用户的 Action
  • 3、Dispatcher 收到 Action,要求 Store 进行相应的更新
  • 4、Store 更新后,发出一个”change”事件
  • 5、View 收到”change”事件后,更新页面

Redux?

前面简短的介绍了Flux的思维,而Redux就是将Flux思维加以实现的。
Redex的设计思想:

1、Web 应用是一个状态机,视图与状态是一一对应的。
2、所有的状态,保存在一个对象里面。

对比前面所讲的React,如果字面意思来说,是差不多的。

下面一一介绍Redux的基本概念:

Store

Store就是一个用来存放数据的地方,整个应用就只能有一个Store,可以使用:

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

可以使用这样的方法来生成store。

store.dispatch()

上面说过,由view去发出Action,从而才可以往下进行。那么view又是如何发Action的呢?
view发出Action的唯一方法就是通过store.dispatch() store就像一个路由器,view给命令给store,由store发出Action。
例如在view中你可以这样写代码:

store.dispatch(addAction('Hello Redux'));

这样以来,view就成功发送一个Action出去了。

store.subscribe()

前面说过,view,是通过store去往外发送Action的,这样一来,在store里面有一个subscribe(fn)的函数,它主要用于监听state,一旦state发生变化,就会执行subscribe注册的这个fn函数。

store里面主要方法:
  • store.getState()
  • store.dispatch()
  • store.subscribe()
生成一个store

如下,实现主要的三个方法的基本store:

const createStore = (reducer) => {
  let state;
  let listeners = [];
  const getState = () => state;

  const dispatch = (action) => {
    state = reducer(state, action);
    listeners.forEach(listener => listener());
  };

  const subscribe = (listener) => {
    listeners.push(listener);
    return () => {
      listeners = listeners.filter(l => l !== listener);
    }
  };

  dispatch({});

  return { getState, dispatch, subscribe };
};

State

React里面也有State,其概念也相同,State就好比某一个时刻的Store,由于State决定这页面的渲染,在一个应用中,一个State,就对应
唯一一个页面。
如下,我们可以这样获得State:

const state = store.getState();

Action

从名字来说,就是动作的意思,一般是由view来发出(触发)的。类似于通知者,view发出通知,用什么发呢?通过Action发。
Action是一个对象,里面的type属性是必须的,其他的可以自由增加。
看一个一般的Action:

const action = {
  type: 'GET_DATA',
  payload: myData
};

上面的这个简单Action意思可以简单理解为:view发出了一个GET_DATA,并且把一份名为payload的数据,也给了Action。接下来的事情就是各种处理,然后更新state从而引发更新view。

那么如何能够产生一个Action呢?
由上面可以知道Action是一个对象,并且有基本的结构,那么就简单了,如下的addAction就是一个Action:

const ADD_TODO = '添加ACTION';
function addAction(text) {
    return {
        type: ADD_TODO,
        text
    }
}

里面除了基本的type外,还有一个text参数。

Reducer

首先: Reducer是一个纯函数
一个纯函数必须满足一下特征:

  • 不得改写参数
  • 不能调用系统 I/O 的API
  • 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果

那么view把Action发送出去后,又会怎么样呢?此时就到了Reducer这里,Reducer是一个函数,它接受Action和当前State作为参数,然后返回一个新的State .
看看一小段代码能够更好的理解:

const reducer = (state = defaultState, action) => {
  switch (action.type) {
    case 'ADD':
      return state + action.payload;
    default: 
      return state;
  }
};

如上,一个简单的reducer就是这样,可能内部有多个Action.type,所以会对应不同的Action的处理。

实际应用中,Reducer 函数不用像上面这样手动调用,store.dispatch方法会触发 Reducer 的自动执行。为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore方法。
store和对应的reducer绑定起来的代码:

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

上面代码中,createStore接受 Reducer 作为参数,生成一个新的 Store。以后每当store.dispatch发送过来一个新的 Action,就会自动调用 Reducer,得到新的 State。

接下来看一张图,从这张图可以很简单的了解到Redux的工作流程:
这里写图片描述

简单描述下上述流程:
- view利用store发送Action
- 然后会到redux里面执行纯函数
- 最终会返回一个新的state
- 由于state改变,从而引发相应component的重新渲染

Redux中的中间件

Redux中间件?前端也需要?
这里回想普通的redux的流程:view发送一个Action,然后reducer就执行,然后就一步一步走下去了,在这里面,Action貌似只代表一个实体,没有任何动作的实体。
那如果,我要先做点事,然后在把结果给reducer去处理呢?
比如ajax请求!
因为reducer是纯函数嘛。
所以就需要先去拿数据,拿完之后,在根据情况发送不同的Action,此时就要引入Redux中间件。

中间件怎么用?

对于引入中间件的操作,还是需要在store里面进行的,以下例子是引入redux-logger 中间件:

import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();

const store = createStore(
  reducer,
  applyMiddleware(logger)
);

上面代码中,redux-logger提供一个生成器createLogger,可以生成日志中间件logger。然后,将它放在applyMiddleware方法之中,传入createStore方法,就完成了store.dispatch()的功能增强。

当然,你也可以引入多个中间件的功能:

  applyMiddleware(thunk, promise, logger)

但是得注意,thunk,promise,logger这三个中间件的引入,是有次序的,具体需要查文档。

applyMiddleware函数
这个函数是干嘛用的呢?
它是 Redux 的原生方法,作用是将所有中间件组成一个数组,依次执行。

redux-thunk 中间件

估计读者记得store里面有个dispatch函数,用来给view发送Action的。而Action是什么呢?是一个对象,构造例如下面:

dispatch({  
    type:CHAT_LOGIN,
    data:req
}); 

redux-thunk能干什么呢?
当把thunk引入后,你可以以这样的一种方式进行dispatch

dispatch(fn)

没错,可以往里面丢一个函数。
看下面一个例子:

const fetchPosts = postTitle => (dispatch, getState) => {
  dispatch(requestPosts(postTitle));
  return fetch(`/some/API/${postTitle}.json`)
    .then(response => response.json())
    .then(json => dispatch(receivePosts(postTitle, json)));
  };
};

// 使用方法一
store.dispatch(fetchPosts('reactjs'));
// 使用方法二
store.dispatch(fetchPosts('reactjs')).then(() =>
  console.log(store.getState())
);

往dispatch里面丢了一个fetchPosts('reactjs'),而fetchPosts('reactjs') 实际上先dispatch了,然后再返回了一个fetch函数,fetch是一个新型的ajax方法。先获取了json,然后再返回一个Action:receivePosts(postTitle, json)

是否有体会到thunk中间件的作用了呢?

React-Redux

关于ReactRedux也了解了差不多了,那么现在应该如何在项目中使用到这个框架呢?
React-Redux将所有的组件分为了UI组件容器组件,
其中,UI组件有以下几个特征:

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

通俗点讲,就是它只负责显示,显示的数据由props提供。
而容器组件则有下面几个特征:

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

容器组件就是来跟后台交互,然后处理数据再返回给UI组件的。

React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。
所以,你只需要编写基本的UI组件,然后把它交给React-Redux就行了。

connect()

如果看过React-Redux项目同学,应该能够发现往往最后一行代码会有connect() 函数,那么这个函数到底有啥作用呢?
connect()函数主要是用于生成容器组件 ,换句话说,就是让UI组件和容器组件连起来 的用途。

看一个简单例子:

import { connect } from 'react-redux'

const VisibleLogin = connect(
  mapStateToProps,
  mapDispatchToProps
)(Login)

上面代码中,可以看到使用了connect()函数,并且返回了一个VisibleLogin,这个就是就是由 React-Redux 通过connect方法自动生成的容器组件

那么问题来了,中间两个mapStateToPropsmapDispatchToProps是啥子东西呢?
前面讲过,UI组件是没有状态的,他只能使用Props,而Action是由用户触发在UI上发送的,现在回过来看看这两个函数,大概有点感觉了吧:

  • 输入逻辑:外部的数据(即state对象)如何转换为 UI 组件的参数
  • 输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去。

    看一个实际中的例子:


let mapStateToProps = ( state ) => {
    let { sessions, user } = state.chatIndex;
    return {
        _sessions: sessions,
        _user: user
    };
};
let mapDispatchToProps = ( dispatch ) => {
    return {
        //把整个Actions都和dispatch绑定起来
        ACTIONS: bindActionCreators( actions, dispatch )
    };
};
//login这个组件。连接,把UI和容器连接。
export default connect( mapStateToProps, mapDispatchToProps )( Login );

mapStateToProps是一个函数。它的作用就是像它的名字那样,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。比如上面代码,在mapStateToProps里面,把state里面的_sessions_user 传递到Props里面了,所以在这个Login组件里面可以使用这两个数据。
mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。

mapDispatchToProps是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。

组件

connect方法生成容器组件以后,必须让容器组件拿到state对象,才能通过mapStateToProps传递给UI组件。
一种方法是将state作为参数,利用子组件的props,一层一层传下去,但这样编程起来比较麻烦。
另一种方法就是利用React-Redux提供的Provider组件,这样就可以让容器组件拿到state了。
这个写起来比较简单:

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from "react-redux";
import Store from "src/store";
import App from 'src/components/App';
import Chat from 'src/pages/Chat/Index';
//由Provider组件传递store,然后一层一层接着可以通过props来往下传递,
ReactDOM.render(
  <Provider store={Store}>
    <App>
    <Chat />
    </App>
  </Provider>,
  document.getElementById('app')
);

一般<Provider>组件是作为顶层组件使用,这样整个页面的所有组件,就都可以得到Store里面的所有东西了。

东西写的有点杂,有哪里不对的地方,欢迎提出来大家一起学习!

参考资料:
http://www.ruanyifeng.com/blog/javascript/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值