React從入門到放棄(3):Redux簡介

安裝

npm i -S redux react-redux redux-devtoolsreact

概念

在redux中分爲3個對象:Action、Reducer、Storenpm

Action

  1. 對行爲(如用戶行爲)的抽象
  2. Action 就是一個普通 JavaScript 對象。如:{ type: 'ADD_TODO', text: 'Go to swimming pool' }(其中type字段是約定也是必須的)
  3. 做爲Reducer的參數

Reducer

  1. 一個普通的函數,用來修改store的狀態。
  2. 函數簽名:(currentState,action)=>newState
    1. 在 default 狀況下返回currentState
    2. Object.assign({},state, { visibilityFilter: action.filter })(第一個參數不能爲state) 等價於ES7的 { ...state, ...newState }
  3. redux 的 combineReducers 方法可合併reducer

Store

  1. 表明數據模型,內部維護了一個state變量
  2. 兩個核心方法,分別是getState、dispatch。前者用來獲取store的狀態(state),後者用來修改store的狀態
  3. createStore(reducer) 可建立Store

redux示例:redux

import { createStore } from 'redux'

// reducer
function counter(state = 0, action) {
    switch (action.type) {
        case 'INCREMENT':
            return state + 1
        default:
            return state
    }
}

// state
let store = createStore(counter); // 會調用

// 監聽
store.subscribe(() =>
    console.log(store.getState())
);

// 調用reducer
store.dispatch({ type: 'INCREMENT' });

react-redux

react的需求:緩存

  1. 數據老是單向從頂層向下分發的
  2. 組件之間的溝統統過提高state:子組件改變父組件state的辦法只能是經過onClick觸發父組件聲明好的回調
  3. state愈來愈複雜:單頁應用的發展致使。包括服務器響應、緩存數據、本地生成還沒有持久化到服務器的數據,也包括 UI 狀態,如激活的路由,被選中的標籤,是否顯示加載動效或者分頁器等等。

react-redux 將react組件分爲2種:展現組件 和 容器組件服務器

展現組件

描述如何展現:負責UI樣式的展現app

  1. 數據來源:props
  2. 數據修改:經過props的回調函數
  3. 不直接使用redux

容器組件

描述如何運行:負責數據獲取 和 狀態更新dom

  1. 數據來源:redux state
  2. 數據修改:redux 派發action
  3. 直接使用redux

react-redux 只有2個API:Provider 和 connect異步

Provider

<Provider store>ide

  1. 在原應用組件上包裹一層,使原來整個應用成爲Provider的子組件
  2. 接收Redux的store做爲props,內部經過context對象傳遞給子孫組件上的connect

connect

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(Component)函數

做用:鏈接React組件與 Redux store

mapStateToProps(state, ownProps) : stateProps

  1. 將 store 中的數據做爲 props 綁定到組件上。
  2. 當 state 變化,或者 ownProps 變化的時候,mapStateToProps 都會被調用,計算出一個新的 stateProps

mapDispatchToProps(dispatch, ownProps): dispatchProps:將 dispatch(action) 做爲 props 綁定到組件上

mergeProps:指定 stateProps 以及 dispatchProps 合併到 ownProps 的方式。(默認使用Object.assign)

connect是個高階組件(HOC)大體源碼:

export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
  return function wrapWithConnect(WrappedComponent) {
    class Connect extends Component {
        constructor(props, context) {
            this.store = props.store || context.store
            this.stateProps = computeStateProps(this.store, props)
            this.dispatchProps = computeDispatchProps(this.store, props)
            this.state = { storeState: null }
            // 合併stateProps、dispatchProps、parentProps
            this.updateState()
        }
        shouldComponentUpdate(nextProps, nextState) {
            // 進行判斷,當數據發生改變時,Component從新渲染
            if (propsChanged || mapStateProducedChange || dispatchPropsChanged) {
                this.updateState(nextProps)
                return true
            }
        }
        componentDidMount() {
            this.store.subscribe( () => this.setState({ storeState: this.store.getState() }) )
        }
        render() {
            return (
                <WrappedComponent {...this.nextState} />
            )
        }
      }
      return Connect;
    }
}

react-redux示例:

Counter.js

import React, { Component } from 'react';
import { createStore, bindActionCreators } from 'redux';
import { Provider, connect } from 'react-redux';

function clickReduce(state = { todo: 1 }, action) {
    switch (action.type) {
        case 'click':
            return Object.assign({}, state, { todo: state.todo + 1 });
        default:
            return state;
    }
}

let store = createStore(clickReduce);

class Counter extends Component {
    render() {
        return (
            <div>
                <div>{this.props.todo}</div>
                <button onClick={this.props.clickTodo}>Click</button>
            </div>
        )
    }
}

// 方式1:
export default connect(state => ({ todo: state.todo }),
    dispatch => ({ clickTodo: () => dispatch({ type: 'click' }) }))(Counter)

// 方式2:
export default connect(state => ({ todo: state.todo }),
    dispatch => bindActionCreators({ clickTodo: () => ({ type: 'click' }) }, dispatch))(Counter);

index.js

import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { render } from 'react-dom';
import Clock from './Clock'

render((
<Provider store={store}>
    <Counter />
</Provider>), root);

在redux中,咱們只能dispatch簡單的action對象。 對應的在react-redux中,咱們只能定義同步的reducer方法。 下節將介紹在react-redux如何定義異步方法。讓其更加適用於生產環境。

相關文章
相關標籤/搜索