成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Redux專題:實(shí)用

Big_fat_cat / 3387人閱讀

摘要:在英文中的意思是有效載荷。有一個(gè)動(dòng)作被發(fā)射了顧名思義,替換,這主要是方便開發(fā)者調(diào)試用的。相同的輸入必須返回相同的輸出,而且不能對外產(chǎn)生副作用。怎么辦呢開發(fā)者得手動(dòng)維護(hù)一個(gè)訂閱器,才能監(jiān)聽到狀態(tài)變化,從而觸發(fā)頁面重新渲染。

本文是『horseshoe·Redux專題』系列文章之一,后續(xù)會(huì)有更多專題推出
來我的 GitHub repo 閱讀完整的專題文章
來我的 個(gè)人博客 獲得無與倫比的閱讀體驗(yàn)

Redux是一套精巧而實(shí)用的工具,這也是它在開發(fā)者中如此流行的原因。

所以對待Redux,最重要的就是熟練使用它的主要API,一旦將它了然于胸,就會(huì)對Redux的設(shè)計(jì)思想有一個(gè)全局的認(rèn)識,也就能清楚的判斷自己的應(yīng)用需不需要?jiǎng)隈{Redux出手。

需要注意:咱們默認(rèn)將Redux和React搭配使用,不過Redux不是非得和它在一起的。

Action

要達(dá)成某個(gè)目的,開發(fā)者首先要描述自己的意圖。Action就是用來描述開發(fā)者意圖的。它不是一個(gè)函數(shù),而是一個(gè)普通的對象,通過聲明的類型來觸發(fā)相應(yīng)的動(dòng)作。

我們來看一個(gè)例子:

{
    type: "ADD_TODO_ITEM",
    payload: {
        content: "每周看一本書",
        done: false,
    },
}

Redux官方定義了字段的一些規(guī)范:一個(gè)Action必須包含type字段,同時(shí)一個(gè)Action包含的字段不應(yīng)該超過type、payload、error、meta這四種。

type聲明Action的類型。一般用全大寫的字符串表示,多個(gè)字母用下劃線分隔。

payload在英文中的意思是有效載荷。引申到程序中就是有效字段的意思,也就是說真正用于構(gòu)建應(yīng)用的信息都應(yīng)該放到payload字段里。

error字段并不承載錯(cuò)誤信息,而是一個(gè)出錯(cuò)的token。只有當(dāng)值為true時(shí)才表示出錯(cuò),值為其他或者干脆沒有該字段表示程序運(yùn)行正常。那么錯(cuò)誤信息放哪呢?當(dāng)然是放payload里面,因?yàn)殄e(cuò)誤信息也屬于構(gòu)建應(yīng)用的有效信息。

meta在英文中的意思是。在這里表示除了payload之外的信息。

因?yàn)橐鈭D是通過類型來定義的,所以type字段必不可少,稱某個(gè)對象為一個(gè)Action的標(biāo)志就是它有一個(gè)type字段。

除此之外,一個(gè)動(dòng)作可能包含更為豐富的信息。開發(fā)者可以隨意添加字段,畢竟它就是個(gè)普通對象。不過遵守一定的規(guī)范便于其他開發(fā)者閱讀你的代碼,可以提升協(xié)作效率。

Constants

前面說了type字段一般用全大寫的字符串表示,多個(gè)字母用下劃線分隔。不僅如此,大家還有一個(gè)約定俗成:用一個(gè)結(jié)構(gòu)相同的變量保存該字符串,因?yàn)樗鼤?huì)在多處用到。

const ADD_TODO_ITEM = "ADD_TODO_ITEM";

集中保存這些變量的文件就叫Constants.js

在此,我提出一點(diǎn)異議。如果你覺得不麻煩,那遵循規(guī)范再好不過。但開發(fā)者向來覺得Redux過于繁瑣,如果你也這么覺得,大可不必維護(hù)所謂的Constants。維護(hù)Constants的好處不過是一處更改處處生效,然而字符串和變量是結(jié)構(gòu)相同的,如果字符串作了修改,語意上必然大打折扣,況且type字段一旦定義極少更改,所以視你的協(xié)作規(guī)模和個(gè)人喜好而定,為Redux的繁瑣減負(fù)不是么?

Action Creators

我們知道Action是一個(gè)對象,但是如果多次用到這個(gè)對象,我們可以寫一個(gè)生成Action的函數(shù)。

function addTodoItem(content) {
    return {
        type: ADD_TODO_ITEM,
        payload: { content, done: false },
    };
}

同理,如果你覺得繁瑣,這一步是可以免去的。

異步場景下Action Creators會(huì)大有用處,后面會(huì)講到。

需要注意的是:所謂的Action更確切的說是一個(gè)執(zhí)行動(dòng)作的指令,而不是一個(gè)動(dòng)作。或者我們換一種說法,這里的動(dòng)作指的是動(dòng)作描述,而不是動(dòng)作派發(fā)。

Store

Redux的本質(zhì)不復(fù)雜,就是用一個(gè)全局的外部的對象來存儲(chǔ)狀態(tài),然后通過觀察者模式來構(gòu)建一套狀態(tài)更新觸發(fā)通知的機(jī)制。

這里的Store就是存儲(chǔ)狀態(tài)的容器。

但是呢?它需要開發(fā)者動(dòng)手寫一套邏輯來指導(dǎo)它怎么處理狀態(tài)的更新,這就是后面要講的Reducer,暫且按下不表。

問題是Store怎么接收這套邏輯呢?

import { createStore } from "redux";

import reducer from "./reducer";

const store = createStore(reducer);

看到?jīng)]有,Redux專門有一個(gè)API用來創(chuàng)建Store,它接受三個(gè)參數(shù):reducer、preloadedStateenhancer。

reducer就是處理狀態(tài)更新的邏輯。

preloadedState是初始狀態(tài),如果你需要讓Store一開始不是空對象,那么可以從這里傳進(jìn)去。

enhancer翻譯成中文是增強(qiáng)器,是用來裝載第三方插件以增強(qiáng)Redux的功能的。

怎么存

我們已經(jīng)了解了Action的作用,但是Action只是對動(dòng)作的描述,怎么說它得有個(gè)發(fā)射器吧。這個(gè)發(fā)射器就隱藏在Store里。

執(zhí)行createStore返回的對象包含了一個(gè)函數(shù)dispatch,傳入Action執(zhí)行就會(huì)發(fā)射一個(gè)動(dòng)作。

import React, { Component } from "react";
import store from "./store";
import action from "./action";

class App extends Component {
    render() {
        return (
            
        );
    }
}

export default App;
怎么取

好了我們已經(jīng)發(fā)射了一個(gè)動(dòng)作,假設(shè)現(xiàn)在Store中已經(jīng)有狀態(tài)了,我們怎么把它取出來呢?

直接store.xxx么?

我們先來打印Store這個(gè)對象看看:

{
    dispatch: ? dispatch(action),
    getState: ? getState(),
    replaceReducer: ? replaceReducer(nextReducer),
    subscribe: ? subscribe(listener),
    Symbol(observable): ? observable(),
}

打印出來一堆API,這可咋整?

別著急,茫茫人海中看到一個(gè)叫getState的東西,它就是我們要找的高人吧。插一句,大家注意區(qū)分Store和State的區(qū)別,Store是存儲(chǔ)State的容器。

Redux隱藏了Store的內(nèi)部細(xì)節(jié),所以開發(fā)者只能用getState來獲取狀態(tài)。

訂閱

Redux是基于觀察者模式的,所以它開放了一個(gè)訂閱的API給開發(fā)者,每次發(fā)射一個(gè)動(dòng)作,傳入訂閱器的回調(diào)都會(huì)執(zhí)行。通過它開發(fā)者就能監(jiān)聽動(dòng)作的派發(fā)以執(zhí)行相應(yīng)的邏輯。

import store from "./store";

store.subscribe(() => console.log("有一個(gè)動(dòng)作被發(fā)射了"));
replaceReducer

顧名思義,替換Reducer,這主要是方便開發(fā)者調(diào)試Redux用的。

Reducer

Reducer是Redux的核心概念,因?yàn)镽edux的作者Dan Abramov這樣解釋Redux這個(gè)名字的由來:Reducer+Flux。

其實(shí)Reducer是一個(gè)計(jì)算機(jī)術(shù)語,包括JavaScript中也有用于迭代的reduce函數(shù)。所以我們先來聊聊應(yīng)該怎樣理解Reducer這個(gè)概念。

reduce翻譯成中文是減少,Reducer在計(jì)算機(jī)中的含義是歸并,也是化多為少的意思。

我們來看JavaScript中reduce的寫法:

const array = [1, 2, 3, 4, 5];
const sum = array.reduce((total, num) => total + num);

再來看Redux中Reducer的寫法:

function todoReducer(state = [], action) {
    switch (action.type) {
        case "ADD_TODO_ITEM":
            const { content, done } = action.payload;
            return [...state, { content, done }];
        case "REMOVE_TODO_ITEM":
            const todos = state.filter(todo => todo.content !== action.content);
            return todos;
        default:
            return state;
    }
}

state參數(shù)是一個(gè)舊數(shù)據(jù)集合,action中包含的payload是一個(gè)新的數(shù)據(jù)項(xiàng),Reducer要做的就是將新的數(shù)據(jù)項(xiàng)和舊數(shù)據(jù)集合歸并到一起,返回給Store。這樣看起來Reducer這個(gè)名字起的也沒那么晦澀了是不是?

一個(gè)Reducer接受兩個(gè)參數(shù),第一個(gè)參數(shù)是舊的state,我們返回的數(shù)據(jù)就是用來替換它的,然后風(fēng)水輪流轉(zhuǎn),這次返回的數(shù)據(jù)下次就變成舊的state了,如此往復(fù);第二個(gè)參數(shù)是我們派發(fā)的action。

因?yàn)镽educer的結(jié)構(gòu)類似,都是根據(jù)Action的類型返回相應(yīng)的數(shù)據(jù),所以一般采用switch case語句,如果沒有變動(dòng)則返回舊的state,總之它必須有返回值。

純函數(shù)

Reducer的作用是歸并,也只能是歸并,所以Redux規(guī)定它必須是一個(gè)純函數(shù)。相同的輸入必須返回相同的輸出,而且不能對外產(chǎn)生副作用。

所以開發(fā)者在返回?cái)?shù)據(jù)的時(shí)候不能直接修改原有的state,而是應(yīng)該在拷貝的副本之上再做修改。

多個(gè)Reducer

一個(gè)Reducer只應(yīng)該處理一個(gè)動(dòng)作,可是我們的應(yīng)用不可能只有一個(gè)動(dòng)作,所以一個(gè)典型的Redux應(yīng)用會(huì)有很多Reducer函數(shù)。那么怎么管理這些Reducer呢?

首先來看只有一個(gè)Reducer的情況:

import { createStore } from "redux";

import reducer from "./reducer";

const store = createStore(reducer);

export default store;

如果只有一個(gè)Reducer,那我們只需要將它傳入createStore這個(gè)函數(shù)中,就這么簡單。這時(shí)候Reducer返回的狀態(tài)就是Store中的全部狀態(tài)。

而如果有多個(gè)Reducer,我們就要?jiǎng)佑肦edux的另一個(gè)API了:combineReducers。

const reducers = combineReducers({
    userStore: userReducer,
    todoStore: todoReducer,
});

當(dāng)我們有多個(gè)Reducer,就意味著有多個(gè)狀態(tài)需要交給Store管理,我們就需要子容器來存儲(chǔ)它們,其實(shí)就是對象嵌套對象的意思。combineReducers就是用來干這個(gè)的,它把每一個(gè)Reducer分門別類的與不同的子容器對應(yīng)起來,某個(gè)Reducer只處理對應(yīng)的狀態(tài)。

{
    userStore: {},
    todoStore: {},
};

當(dāng)我們用getState獲取整個(gè)Store的狀態(tài),返回的對象就是上面這樣的。

你猜對了,傳入combineReducers的對象的key就是子容器的名字。

默認(rèn)值

當(dāng)開發(fā)者調(diào)用createStore創(chuàng)建Store時(shí),傳入的所有Reducer都會(huì)執(zhí)行一遍。注意,這時(shí)開發(fā)者還沒有發(fā)射任何動(dòng)作呢,那為什么會(huì)執(zhí)行一遍?

const randomString = () => Math.random().toString(36).substring(7).split("").join(".");

const ActionTypes = {
    INIT: `@@redux/INIT${randomString()}`,
    REPLACE: `@@redux/REPLACE${randomString()}`,
    PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
};

dispatch({ type: ActionTypes.INIT });

因?yàn)镽edux源碼中,在createStore函數(shù)里面放了這樣一段邏輯,這初始化時(shí)的dispatch是Redux自己發(fā)射的。

為什么?

還記得Reducer接受兩個(gè)參數(shù)嗎?第一個(gè)是state,而我們可以給state設(shè)置默認(rèn)值。

聰明的你一定想到了,初始化Store時(shí)Redux自己發(fā)射一個(gè)動(dòng)作的目的是為了收集這些默認(rèn)值。Reducer會(huì)將這些默認(rèn)值返回給Store,這樣默認(rèn)值就保存到Store中了。

聰明的你大概還想到一個(gè)問題:createStore也有默認(rèn)值,Reducer也有默認(rèn)值,不會(huì)打架么?

Redux的規(guī)矩:createStore的默認(rèn)值優(yōu)先級更高,所以不會(huì)打架。

執(zhí)行

在一個(gè)有若干Reducer的應(yīng)用中,一個(gè)動(dòng)作是怎么找到對應(yīng)的Reducer的?

這是一個(gè)好問題,答案是挨個(gè)找。

假如應(yīng)用有1000個(gè)Reducer,與某個(gè)動(dòng)作對應(yīng)的Reducer又恰好在最后一個(gè),那要把1000個(gè)Reducer都執(zhí)行一遍,Redux不會(huì)這么傻吧?

Redux還真就這么傻。

因?yàn)楫?dāng)一個(gè)動(dòng)作被派發(fā)時(shí),Redux并不知道應(yīng)該由哪個(gè)Reducer來處理,所以只能讓每個(gè)Reducer都處理一遍,看看到底是誰的菜。可不可以在設(shè)計(jì)上將動(dòng)作與Reducer對應(yīng)起來呢?當(dāng)然是可以的,但是Redux為了保證API的簡潔和優(yōu)美,決定犧牲這一部分性能。

只是一些純函數(shù)而已,莫慌。

react-redux

當(dāng)我們使用Redux時(shí),我們希望每發(fā)射一個(gè)動(dòng)作,應(yīng)用的狀態(tài)自動(dòng)發(fā)生改變,從而觸發(fā)頁面的重新渲染。

import React, { Component } from "react";
import store from "./store";

class App extends Component {
    state = { name: "Redux" };

    render() {
        const { name } = this.state;
        return (
            
{name}
); } componentDidMount() { this.unsubscribe = store.subscribe(() => { const { name } = store.getState(); this.setState({ name }); }); } componentWillUnmount() { this.unsubscribe(); } }

怎么辦呢?開發(fā)者得手動(dòng)維護(hù)一個(gè)訂閱器,才能監(jiān)聽到狀態(tài)變化,從而觸發(fā)頁面重新渲染。

但是React最佳實(shí)踐告訴我們,一個(gè)負(fù)責(zé)渲染UI的組件不應(yīng)該有太多的邏輯,那么有沒有更好的辦法使得開發(fā)者可以少寫一點(diǎn)邏輯,同時(shí)讓組件更加優(yōu)雅呢?

別擔(dān)心,Redux早就幫開發(fā)者做好了,不過它是一個(gè)獨(dú)立的模塊:react-redux。顧名思義,這個(gè)模塊的作用是連接React和Redux。

Provider

連接React和Redux的第一步是什么呢?當(dāng)然是將Store集成到React組件中,這樣我們就不用每次在組件代碼中import store了。多虧了React context的存在,Redux只需要將Store傳入根組件,所有子組件就能通過某種方式獲取傳入的Store。

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";

import store from "./store";

import App from "./App";

ReactDOM.render(
    
        
    
    ,
    document.getElementById("root")
);
connect

老式的context寫法,在子組件中定義contextTypes就可以接收到傳入的參數(shù)。當(dāng)然,你肯定也想到,Redux把這些細(xì)節(jié)都封裝好了,這就是connect

connect接口的意義主要有三點(diǎn):

封裝用context從根組件獲取數(shù)據(jù)的細(xì)節(jié)。

封裝Redux訂閱器的細(xì)節(jié)。

作為一個(gè)容器組件真正連接React和Redux。

import React from "react";
import Todo from "./Todo";

const App = ({ todos, addTodoItem }) => {
    return (
        
{todos.map(todo => )}
); } const mapStateToProps = (state, ownProps) => { return { todos: state.todoStore, }; }; const mapDispatchToProps = (dispatch, ownProps) => { return { addTodoItem: (todoItem) => dispatch({ type: "ADD_TODO_ITEM", payload: todoItem }), }; }; export default connect(mapStateToProps, mapDispatchToProps)(App);

我們看上面例子,connect接受的兩個(gè)參數(shù):mapStateToPropsmapDispatchToProps,所謂的map就是映射,意思就是將所有state和dispatch依次映射到props上。如此真正的組件需要的數(shù)據(jù)和功能都在props上,它就可以安安心心的做一個(gè)傻瓜組件。

connect接受四個(gè)參數(shù):

mapStateToProps。也可以寫成mapState,這個(gè)參數(shù)是用來接收訂閱得到的數(shù)據(jù)更新的,也就是說如果這個(gè)參數(shù)傳null或者undefined,則被connect包裹的組件無法收到更新的數(shù)據(jù)。mapStateToProps必須是一個(gè)函數(shù),而且必須返回一個(gè)純對象。它接收兩個(gè)參數(shù),第一個(gè)參數(shù)是存儲(chǔ)在Store中完整的state,第二個(gè)參數(shù)是被connect包裹的組件自身的屬性。假如App組件掛載時(shí)寫成這樣:,那么ownProps就是一個(gè)包含value的對象。

mapDispatchToProps。也可以寫成mapDispatch,這個(gè)參數(shù)是用來封裝所有發(fā)射器的。mapDispatchToProps必須是一個(gè)函數(shù),而且必須返回一個(gè)純對象。它接收兩個(gè)參數(shù),第一個(gè)參數(shù)是dispatch發(fā)射器函數(shù),第二個(gè)參數(shù)和mapStateToProps的第二個(gè)參數(shù)相同。

mergeProps。顧名思義,合并props?,F(xiàn)在被connect包裹的組件擁有三種props:由state轉(zhuǎn)化而來的props,由dispatch轉(zhuǎn)化而來的props,自身的props。它返回的純對象就是最終組件能接收到的props。默認(rèn)返回的對象是用Object.assign()合并上述三種props。

options。用來自定義connect的選項(xiàng)。

我們注意到,connect要先執(zhí)行一次,返回的結(jié)果再次執(zhí)行才傳入開發(fā)者定義的組件。它返回一個(gè)新的組件,這個(gè)新的組件不會(huì)修改原組件(除非你操縱了ownProps的返回),而是為組件增加一些新的props。

我們也可以用裝飾器寫法來重寫connect:

import React from "react";
import Todo from "./Todo";

const mapStateToProps = (state, ownProps) => {
    return {
        todos: state.todoStore,
    };
};

const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        addTodoItem: (todoItem) => dispatch({ type: "ADD_TODO_ITEM", payload: todoItem }),
    };
};

@connect(mapStateToProps, mapDispatchToProps)
const App = ({ todos, addTodoItem }) => {
    return (
        
{todos.map(todo => )}
); } export default App;
總結(jié)

Redux通過調(diào)用createStore返回Store,它是一個(gè)獨(dú)立于應(yīng)用的全局對象,通過觀察者模式能讓應(yīng)用監(jiān)聽到Store中狀態(tài)的變化。最佳實(shí)踐是一個(gè)應(yīng)用只有一個(gè)Store。

Redux必須通過一個(gè)明確的動(dòng)作來修改Store中的狀態(tài),描述動(dòng)作的是一個(gè)純對象,必須有type字段,傳遞動(dòng)作的是Store的屬性方法dispatch。

Store本身并沒有任何處理狀態(tài)更新的邏輯,所有邏輯都要通過Reducer傳遞進(jìn)來,Reducer必須是一個(gè)純函數(shù),沒有任何副作用。如果有多個(gè)Reducer,則需要利用combineReducers定義相應(yīng)的子狀態(tài)容器。

基于容器組件和展示組件分離的設(shè)計(jì)原則,也為了提高開發(fā)者的編程效率,Redux通過一個(gè)額外的模塊將React和Redux連接起來,使得所有的狀態(tài)管理接口都映射到組件的props上。其中,Provider將Store注入應(yīng)用的根組件,解決的是連接的充分條件;connect將需要用到的state和dispatch都映射到組件的props上,解決的是連接的必要條件。只有被Provider包裹的組件,才能使用connect包裹。

Redux專題一覽

考古
實(shí)用
中間件
時(shí)間旅行

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/97919.html

相關(guān)文章

  • Redux專題:考古

    摘要:光憑一個(gè)是無法實(shí)現(xiàn)血緣關(guān)系疏遠(yuǎn)的組件之間的狀態(tài)同步的。就是為解決這個(gè)問題而生的。,處理動(dòng)作的派發(fā),相當(dāng)于架構(gòu)的。我們的主角是,它也是目前社區(qū)最受歡迎的狀態(tài)管理框架。專題一覽考古實(shí)用中間件時(shí)間旅行 本文是『horseshoe·Redux專題』系列文章之一,后續(xù)會(huì)有更多專題推出來我的 GitHub repo 閱讀完整的專題文章來我的 個(gè)人博客 獲得無與倫比的閱讀體驗(yàn) React的橫空出世給...

    toddmark 評論0 收藏0
  • Redux專題:中間件

    摘要:好處就是不再需要能夠處理異步的中間件了。不過,它是一個(gè)研究中間件很好的范本。執(zhí)行它,返回的是由第二層函數(shù)組成的中間件數(shù)組。也就是說呀同學(xué)們,除了最后一個(gè)中間件的是原始的之外,倒數(shù)往前的中間件傳入的都是上一個(gè)中間件的邏輯函數(shù)。 本文是『horseshoe·Redux專題』系列文章之一,后續(xù)會(huì)有更多專題推出來我的 GitHub repo 閱讀完整的專題文章來我的 個(gè)人博客 獲得無與倫比的閱...

    ybak 評論0 收藏0
  • 2017年3月份前端資源分享

    平日學(xué)習(xí)接觸過的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個(gè)網(wǎng)址:http://www.kancloud.cn/jsfron... 03月份前端資源分享 1. Javascript 175453545 Redux compose and middleware 源碼分析 深入 Promise(二)——進(jìn)擊的 Promise Effective JavaScript leeheys blog -...

    ermaoL 評論0 收藏0
  • 2017年3月份前端資源分享

    平日學(xué)習(xí)接觸過的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個(gè)網(wǎng)址:http://www.kancloud.cn/jsfron... 03月份前端資源分享 1. Javascript 175453545 Redux compose and middleware 源碼分析 深入 Promise(二)——進(jìn)擊的 Promise Effective JavaScript leeheys blog -...

    kamushin233 評論0 收藏0

發(fā)表評論

0條評論

Big_fat_cat

|高級講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<