Redux詳細(xì)入門教程

你可能不需要 Redux

首先明確一點(diǎn),Redux 是一個(gè)有用的架構(gòu),但不是非用不可。事實(shí)上,大多數(shù)情況,你可以不用它,只用 React 就夠了。

魯迅(∩_∩) 曾經(jīng)說過: <font color="red">"如果你不知道是否需要 Redux,那就是不需要它。"</font>
Redux 的創(chuàng)造者 Dan Abramov 又補(bǔ)充了一句:<font color="red">"只有遇到 React 實(shí)在解決不了的問題,你才需要 Redux 。"</font>

簡(jiǎn)單說,如果你的UI層非常簡(jiǎn)單,沒有很多互動(dòng),Redux 就是不必要的,用了反而增加復(fù)雜性。

不需要使用 Redux:

  • 用戶的使用方式非常簡(jiǎn)單
  • 用戶之間沒有協(xié)作
  • 不需要與服務(wù)器大量交互,也沒有使用 WebSocket
  • 視圖層(View)只從單一來源獲取數(shù)據(jù)

需要使用 Redux:

  • 組件需要共享數(shù)據(jù)(或者叫做狀態(tài)state)的時(shí)候
  • 某個(gè)狀態(tài)需要在任何地方都可以被隨時(shí)訪問的時(shí)候
  • 某個(gè)組件需要改變另一個(gè)組件的狀態(tài)的時(shí)候
  • 語言切換、暗黑模式切換、用戶登錄全局?jǐn)?shù)據(jù)共享 ...

簡(jiǎn)介

Redux 是 js 應(yīng)用的可預(yù)測(cè)狀態(tài)的容器。 可以理解為全局?jǐn)?shù)據(jù)狀態(tài)管理工具(狀態(tài)管理機(jī)),用來做組件通信等。

Redux 的設(shè)計(jì)思想很簡(jiǎn)單,就兩句話:

  1. Web 應(yīng)用是一個(gè)狀態(tài)機(jī),視圖與狀態(tài)是一一對(duì)應(yīng)的。

  2. 所有的狀態(tài),保存在一個(gè)對(duì)象里面。

image

Redux架構(gòu)原理:

  1. 剝離組件數(shù)據(jù)(state)
  2. 數(shù)據(jù)統(tǒng)一存放在store中
  3. 組件訂閱store獲得數(shù)據(jù)
  4. store推送數(shù)據(jù)更新

類似與vue的狀態(tài)管理vuex

一句話總結(jié):Redux統(tǒng)一保存了數(shù)據(jù),在隔離了數(shù)據(jù)與UI的同時(shí),負(fù)責(zé)處理數(shù)據(jù)的綁定。


安裝

yarn add redux

npm install redux --save


原則

  1. store是唯一的 (整個(gè)應(yīng)用只能有一個(gè) store)
  2. 只有store能改變自己的內(nèi)容 (store里的數(shù)據(jù)不是reducer更新的)
  3. reducer必須是純函數(shù) (只要是同樣的輸入,必定得到同樣的輸出)

核心API

image

1. Store

Store 就是保存全局?jǐn)?shù)據(jù)的地方,你可以把它看成一個(gè)容器(帶有推送功能的數(shù)據(jù)倉庫)。整個(gè)應(yīng)用只能有一個(gè) Store。

Redux 提供createStore這個(gè)函數(shù),用來生成 Store。

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

.

2. State

Store對(duì)象包含所有數(shù)據(jù)。如果想得到某個(gè)時(shí)點(diǎn)的數(shù)據(jù),就要對(duì) Store 生成快照。這種時(shí)點(diǎn)的數(shù)據(jù)集合,就叫做 State。

當(dāng)前時(shí)刻的 State,可以通過store.getState()拿到。

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

const state = store.getState();

Redux 規(guī)定, 一個(gè) State 對(duì)應(yīng)一個(gè) View。只要 State 相同,View 就相同。你知道 State,就知道 View 是什么樣,反之亦然。

.

3. Action

State 的變化,會(huì)導(dǎo)致 View 的變化。但是,用戶接觸不到 State,只能接觸到 View。所以,State 的變化必須是 View 導(dǎo)致的。

Action 就是 View 發(fā)出的通知,表示 State 應(yīng)該要發(fā)生變化了。

Action 是一個(gè)對(duì)象。其中的type屬性是必須的,表示 Action 的名稱。其他屬性可以自由設(shè)置,社區(qū)有一個(gè)規(guī)范可以參考。

const action = {
    type: 'change_input',
    value: 'Learn Redux',
};

上面代碼中,Action 的名稱是change_input,它攜帶的信息是字符串Learn Redux。

可以這樣理解,Action 描述當(dāng)前發(fā)生的事情。<font color="red">改變 State 的唯一辦法,就是使用 Action。它會(huì)運(yùn)送數(shù)據(jù)到 Store。</font>

.

4. Action Creator

過多的Action在組件中,會(huì)顯得代碼過于臃腫,不利于閱讀和維護(hù)
可以新建一個(gè)actionCreator.js文件,用來統(tǒng)一管理action

src/store/actionCreator.js

// action的統(tǒng)一管理

export const changeInputAction = value => ({
    type: 'change_input',
    value,
});

export const addItemAction = () => ({
    type: 'add_item',
});

export const delItemAction = index => ({
    type: 'del_item',
    index,
});

export const initListAction = list => ({
    type: 'init_list',
    list,
});

每個(gè)函數(shù)都用來返回一個(gè)action,這個(gè)函數(shù)就叫 Action Creator。

.

5. store.dispatch()

store.dispatch()是 View 發(fā)出 Action 的唯一方法

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

const action = {
    type: 'change_input',
    value: 'Learn Redux',
};

store.dispatch(action);

結(jié)合 Action Creator,這段代碼可以改寫如下:

import { createStore } from 'redux';
import { changeInputAction } from 'src/store/actionCreator.js'

const store = createStore(fn);

store.dispatch(changeInputAction('Learn Redux'));

.

6. Reducer

Store 收到 Action 以后,必須給出一個(gè)新的 State,這樣 View 才會(huì)發(fā)生變化。這種 State 的計(jì)算過程就叫做 Reducer。

Reducer 是一個(gè)函數(shù),它接受 Action 和當(dāng)前 State 作為參數(shù),返回一個(gè)新的 State。

可以新建一個(gè)reducer.js文件,用來統(tǒng)一管理

src/store/reducer.js

// 初始狀態(tài),作為 State 的默認(rèn)值
const defaultState = {
    value: ''
};

const reducer = (state = defaultState, action) => {
    let newState = JSON.parse(JSON.stringify(state)); // 深拷貝,不能直接修改state里的數(shù)據(jù)
    if (action.type === 'change_input') {
        newState.value = action.value;
    }
    if (action.type === 'add_item') {
        newState.list.push(newState.value);
        newState.value = '';
    }
    if (action.type === 'del_item') {
        newState.list.splice(action.index, 1);
    }
    if (action.type === 'init_list') {
        newState.list = action.list;
    }
    return newState;
};

export default reducer;

reducer函數(shù)收到名為change_input、add_item、del_item、init_list的 Action 后,就返回一個(gè)新的 State(newState),作為結(jié)果拋出。

在生成 Store 的時(shí)候,將 reducer傳入createStore()。以后每當(dāng)store.dispatch發(fā)送過來一個(gè)新的 Action,就會(huì)自動(dòng)調(diào)用 reducer,得到新的 State

import { createStore } from 'redux';
import reducer from 'src/store/reducer.js';

const store = createStore(reducer);

.

7. store.subscribe()

Store 允許使用store.subscribe()方法設(shè)置監(jiān)聽函數(shù),一旦 State 發(fā)生變化,就自動(dòng)執(zhí)行這個(gè)函數(shù)。

import { Component} from 'react';
import store from './store/index';

class List extends Component {
    constructor(props) {
        super(props);
        store.subscribe(this.storeChange.bind(this)); // store發(fā)生改變時(shí),自動(dòng)觸發(fā)storeChange方法
    }
    render() {
        return (
            // ....
        );
    }
    storeChange() {
        // store發(fā)生改變時(shí),將自動(dòng)觸發(fā)
        let newState = store.getState();  // 得到當(dāng)前state狀態(tài),包含所有store屬性
        this.setState(newState);  // 重新渲染 View
    }
}

export default List;

store.subscribe方法返回一個(gè)函數(shù),調(diào)用這個(gè)函數(shù)就可以解除監(jiān)聽

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

unsubscribe(); // 解除監(jiān)聽

工作流程

image

1. 用戶發(fā)出 Action

const action = {
    type: 'change_input',
    value: 'Learn Redux',
};

store.dispatch(action);

2. Store 自動(dòng)調(diào)用 Reducer,并且傳入兩個(gè)參數(shù):當(dāng)前 State 和收到的 Action。 Reducer 會(huì)返回新的 State(newState)

const reducer = (state, action) => {
    let newState = JSON.parse(JSON.stringify(state)); // 深拷貝,不能直接修改state里的數(shù)據(jù)
    if (action.type === 'change_input') {
        newState.value = action.value;
    }
    return newState;
};

export default reducer;

3. State 一旦有變化,Store 就會(huì)調(diào)用監(jiān)聽函數(shù)

store.subscribe(this.storeChange.bind(this)); // store發(fā)生改變時(shí),自動(dòng)觸發(fā)storeChange方法

4.storeChange()可以通過store.getState()得到當(dāng)前狀態(tài)。如果使用的是 React,這時(shí)可以觸發(fā)重新渲染 View

storeChange() {
    // store發(fā)生改變時(shí),將自動(dòng)觸發(fā)
    let newState = store.getState();  // 得到當(dāng)前state狀態(tài),包含所有store屬性
    this.setState(newState);  // 重新渲染 View
}

完整實(shí)例

src/store/index.js

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

const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__());

export default store;

src/list.js

import store from './store/index';
import { changeInputAction } from './store/actionCreator';

class List extends Component {
    constructor(props) {
        super(props);
        this.state = store.getState();  // 獲取store中所有的數(shù)據(jù)
        store.subscribe(this.storeChange.bind(this)); // store發(fā)生改變時(shí),自動(dòng)觸發(fā)
    }
    render() {
        return (
            <div>
                <input value={this.state.value} onChange={this.change.bind(this)} />
            </div>
        );
    }
    storeChange() {
        this.setState(store.getState());
    }
    change(e) {
        const action = changeInputAction(e.target.value);
        store.dispatch(action); // 派發(fā)action給store
    }
}

export default List;

src/store/actionCreator.js

export const changeInputAction = value => ({
    type: 'change_input',
    value,
});

src/store/reducer.js

const defaultState = {
    value: ''
};

const reducer = (state = defaultState, action) => {
    console.log(state, action);
    let newState = JSON.parse(JSON.stringify(state)); // 深拷貝,不能直接修改state里的數(shù)據(jù)
    if (action.type === 'change_input') {
        newState.value = action.value;
    }
    return newState;
};

export default reducer;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容