溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Redux的核心概念,實現代碼與應用示例

發布時間:2020-05-22 09:29:48 來源:網絡 閱讀:468 作者:xiao_lili 欄目:web開發

Redux是一種JavaScript的狀態管理容器,是一個獨立的狀態管理庫,可配合其它框架使用,比如React。引入Redux主要為了使JavaScript中數據管理的方便,易追蹤,避免在大型的JavaScript應用中數據狀態的使用混亂情況。Redux 試圖讓 state 的變化變得可預測,為此做了一些行為限制約定,這些限制條件反映在 Redux 的三大原則中。

本文會介紹Redux的幾個基本概念和堅持的三大原則,以及完整的回路一下Redux中的數據流。在了解以上這些概念之后,用自己的代碼來實現一個簡版的Redux,并且用自己實現的Redux結合React框架,做一個簡單的TodoList應用示例。希望本文對于初識Redux的同學有一個清晰,全面的認識。

Redux的幾個基本概念

一、數據存儲 - state

Redux就是用來管理狀態數據,所以第一個概念就是狀態數據,state就是存放數據的地方,根據應用需要,一般定義成一個對象,比如:

{
????todos:?[],
????showType:?'ALL',
????lastUpdate:?'2019-10-30?11:56:11'
}

?

二、行為觸發 - action

web應用,所有的數據狀態變更,都是由一個行為觸發的,比如用戶點擊,網絡加載完成,或者定時事件。在簡單應用里面,我們一般都是在行為觸發的時候,直接修改對應的數據狀態,但是在大型復雜的應用里面,修改同一數據的地方可能很多,每個地方直接修改,會造成數據狀態不可維護。

Redux引入了action的概念,每個要改變數據狀態的行為,都定義成一個action對象,用一個type來標志是什么行為,行為附帶的數據,也都直接放在action對象,比如一個用戶輸入的行為:

{
????type:?'INPUT_TEXT',
????text:?'今天下午6點活動碰頭會議'
}

然后通過dispatch觸發這個action,dispatch(action)

三、行為響應 - reducer

狀態,action的概念了解了,當action觸發的時候,肯定要修改state數據,在講解action的時候有說過,不能直接修改state,我們需要定義一個reducer來修改數據,這個reducer就是一個行為響應函數,他接收當前state,和對應的action對象,根據不同的action,做相應的邏輯判斷和數據處理,然后返回一個新的state。

注意,一定是返回一個新的state,不能直接修改參數傳入的原state,這是redux的原則之一,后面會講到。

function?reducer?(?state?=?[],?action?)?{
????switch?(?action.type?)?{
????????case?'INPUT_TEXT':
????????????return?[...state,?{text:?action.text,?id:?Math.random()?}]
????????default:
????????????return?state;
????}
}

?

四、數據監聽 - subscribe

數據的更新已經在reducer中完成了,在一些響應式的web應用中,我們往往需要監聽數據狀態的變化,這個時候就可以用subscribe了

redux內部保存一個監聽隊列,listeners,可以調用subscribe來往listeners里面增加新的監聽函數,每次reducer修改完state之后,會逐個執行監聽函數,而監聽函數可以獲取已經更新過的state數據了

listeners?=?[];
subscrible(?listener?)?{
????listeners.push(?listener?);
????return?function?()?{
????????let?index?=?listeners.index(?listener?);
????????listeners.splice(?index,?1?);
????}
}
dispatch(?action?)?//?觸發?action
reducer(state,?action)

listeners.map(?(?listener?)?=>?{
????listener()
}?)

?

Redux的幾大原則

一、單一數據原則

整個應用的數據都在state,并且只有這一個state,這么做的目的是方便管理,整個應用的數據就這一份,調試方便,開發也方便,可以在開發的時候用本地的數據。而且開發同構應用也很方便,比如服務端渲染,把服務端的數據全部放在state,作為web端初始化時候的數據

二、state只讀

state的數據對外只讀,不能直接修改state,唯一可以修改的方式是觸發action,然后通過reducer來處理。

因為所有的修改都被集中化處理,且嚴格按照一個接一個的順序執行,因此不用擔心競態條件(race?condition)的出現。?Action?就是普通對象而已,因此它們可以被日志打印、序列化、儲存、后期調試或測試時回放出來。

三、使用純函數

先說明下什么是純函數,純函數指的是函數內部不修改傳入的參數,無副作用,在傳參一定的情況下,返回的結果也是一定的。Redux中的Reducer需要設計成存函數,不能直接操作傳入的state,需要把改變的數據以一個新的state方式返回。

Redux中的數據流

其實上面講Redux基本概念的時候已經大概的說了下數據流向方式了,就是: view->action->reducer->state->view,用文字來表述就是,首先由于頁面上的某些事件會觸發action,通過dispatch(action)來實現,然后通過reducer處理,reducer(state, action)返回一個新的state,完成state的更新,當然對于響應式的應用,會觸發listener(),在listener里面獲取最新的state狀態,完成對應視圖(view)的更新。這就是整個redux中的數據流描述,如下圖所示:

Redux的核心概念,實現代碼與應用示例

Redux的實現代碼(非官方)

在對Redux的基本概念和幾大原則熟悉了之后,可以實現一個自己的Redux了,當然我們一般都直接用官方的npm包,這里自己實現的比較簡單,沒有做什么入參驗證,異常處理之類的,主要是加深下對Redux的理解。下面直接貼代碼了,對應的概念都有注釋。

//?redux.js
//?創建state的函數
//?傳入reducer?和初始化的state
function?createStore(?reducer,?initState?)?{
????let?ref?=?{};
????let?listeners?=?[];
????let?currentState?=?initState;

????//?dispath函數,用來觸發action
????function?dispatch?(?action?)?{
????????//?觸發的action,通過reducer處理
????????currentState?=?reducer(?currentState,?action?)

????????//?處理完成后,通知listeners
????????for?(?let?i?in?listeners?)?{
????????????let?listener?=?listener[?i?];
????????????listener();
????????}
????????return?action;
????}

????//?返回當前的state
????function?getState?()?{
????????return?currentState;
????}

????//?訂閱state變化,?傳入listener,返回取消訂閱的function
????function?subscribe?(?listener?)?{
????????listeners.push(?listener?);
????????return?function?()?{
????????????let?index?=?listeners.indexOf(?listener?);
????????????if?(?index?>?-1?)?{
????????????????listeners.splice(?index,?1?);
????????????}
????????}
????}
????
????ref?=?{
????????dispatch:?dispatch,
????????subscribe:?subscribe,
????????getState:?getState
????};
????return?ref;
}

function?combineReducers(?reducers?)?{
????return?function?(?state,?action?)?{
????????let?finalState?=?{};
????????let?hasChanged?=?false;
????????for?(?let?key?in?reducers?)?{
????????????let?reducer?=?reducers[?key?]
????????????if?(?typeof?reducer?===?'function'?)?{
????????????????let?keyState?=?reducer(?state?&&?state[?key?],?action?);
????????????????hasChanged?=?hasChanged?||?keyState?!==?state[?key?];
????????????????finalState[?key?]?=?keyState;
????????????}
????????}
????????return?hasChanged???finalState?:?state;
????}
}

export?{?createStore,?combineReducers?}

是不是覺得怎么才這么點代碼,就是這么點代碼,而且還包含了一個combineReducers輔助函數,下面再貼一點使用示例代碼

//?reducer函數,用于處理action
function?reducer(?state?=?[],?action?)?{
????switch(?action.type?)?{
????????case?'INPUT_TEXT':
????????????return?[?...state,?{?text:?action.text,?key:?Math.random(),?isDo:?false?}];
????????case?'TOGGLE_TODO':
????????????return?state.map(?(?item?)?=>?{
????????????????if?(?item.key?===?action.id?)?{
????????????????????return?{...item,?isDo:?!item.isDo?};
????????????????}
????????????}?);
????????default:
????????????return?state;
????}
}

let?store?=?createStore(?reducer?);

//?在用戶輸入一條Todo時候
console.log(store.getState());
store.dispatch(?{?type:?'INPUT_TEXT',?text:?'這里是一條待辦事項'?}?);
console.log(store.getState());

//在用戶點擊一條Todo?Item的時候,切換完成狀態
console.log(store.getState());
store.dispatch(?{?type:?'TOGGLE_TODO',?id:?item.key?}?)
console.log(store.getState());

?

Redux與React的結合應用示例

下面,利用Redux結合React開發一個簡單的Todo工具,頁面主要功能點

1、可以添加Todo事項

2、點擊事項會切換事項的完成狀態

3、可以切換展示全部/已完成/待完成事項

這個實例是基于react,react-redux完成的,項目搭建用的是create-react-app,利用react-redux提供的接口,將redux中的state和action集成到組件中,需要讀者熟悉create-react-app的使用,以及react-redux的主要接口功能,以下貼出主要代碼,感興趣的同學可以自己搭建實現

首先定義好state數據結構和action以及對應的reducer

state包含兩部分,一是todos,待辦事項列表,二是showType,展示類型

action包含這么三種,一是添加新的Todo,二是切換事項完成狀態,三是切換展示類型,分別定義好

actions.js

//?actions.js
let?nextTodoId?=?0

export?const?addTodo?=?text?=>?{
????return?{
????????type:?'ADD_TODO',
????????id:?nextTodoId++,
????????text
????};
};

export?const?setShowType?=?showType?=>?{
????return?{
????????type:?"SET_SHOW_TYPE",
????????showType
????};
};

export?const?toggleTodo?=?id?=>?{
????return?{
????????type:?'TOGGLE_TODO',
????????id
????};
};

reducers.js

const?todos?=?(?state?=?[],?action?)?=>?{
????switch?(?action.type?)?{
????????case?'ADD_TODO':
????????????return?[
????????????????...state,
????????????????{
????????????????????id:?action.id,
????????????????????text:?action.text,
????????????????????isDo:?false
????????????????}
????????????];
????????case?'TOGGLE_TODO':
????????????return?state.map(?todo?=>?{
????????????????return?todo.id?===?action.id???{...todo,?isDo:?!todo.isDo?}?:?todo;
????????????}?);
????????default:
????????????return?state;
????}
}

const?showType?=?(?state?=?'SHOW_ALL',?action?)?=>?{
????switch?(?action.type?)?{
????????case?'SET_SHOW_TYPE':
????????????return?action.showType;
????????default:
????????????return?state;
????}
}

const?todoList?=?combineReducers({
????todos,
????showType
})
export?{?todoList?}

?

至此,數據狀態redux部分算完成了,接下來實現對應的Component和入口文件了,準備分這么幾個組件

1、待辦事項Todo

2、輸入框 AddTodo

3、待辦事項列表TodoList

4、底部展示類型切換Tab

//?component.js
import?{?connnect?}?from?'react-redux';
import?{?addTodo,?setShowType,?toggleTodo?}?from?'./actions'

const?Todo?=?(?{?onClick,?completed,?text?}?)?=>?(
????<li?onClick={onClick}?style={{?textDecoration:?completed???'line-through'?:?'none'?}}>
????????{text}
????</li>
)

const?AddTodo?=?(?{?dispatch?}?)?=>?{
????let?input;
????return?(
????????<div>
????????????<form
????????????????onSubmit={?e?=>?{
????????????????????e.preventDefault()
????????????????????if?(?!input.value.trim()?)?{
????????????????????????return;
????????????????????}
????????????????????dispatch(?addTodo(?input.value?)?)
????????????????????input.value?=?''
????????????????}}
????????????>
????????????????<input?ref={?node?=>?{input?=?node?}?}?/>
????????????????<button?type='submit'>Add?Todo</button>
????????????</form>
????????</div>
????)
}
AddTodo?=?connect()(?AddTodo?);

const?TodoList?=??(?{?todos,?onTodoClick?}?)?=>?{
????return?(
????????<ul>
????????????{todos.map(?todo?=>?(
????????????????<Todo?key={todo.id}?{...todo}?onClick={?()?=>?onTodoClick(?todo.id?)?}?/>
????????????)?)}
????????</ul>
????)?};
????
const?getShowTodoList?=?(?todos,?showType?)?=>?{
????switch(?showType?)?{
????????case?'SHOW_ISDO':
????????????return?todos.filter(?item?=>?item.isDo?);
????????case?'SHOW_ACTIVE':
????????????return?todos.filter(?item?=>?!item.isDo?);
????????case?'SHOW_ALL':
????????default?:
????????????return?todos;
????}
}

const?mapStateToProps?=?state?=>?{
????return?{
????????todos:?getShowTodoList?(?state.todos,?state.showType)
????};
};

const?mapDispatchToProps?=?dispatch?=>?{
????return?{
????????onTodoClick:?id?=>?{
????????????dispatch(?toggleTodo(?id?)?);
????????}
????};
}

const?ShowTodoList?=?connect(
????mapStateToProps,
????mapDispatchToProps
)(?TodoList?);
???
?const?Tab?=?()?=>?(
????<p>
????????Show:?{?'?'?}
????????<FilterLink?filter='SHOW_ALL'>ALL</FilterLink>
????????{?',?'?}
????????<FilterLink?filter='SHOW_ACTIVE'>ACTIVE</FilterLink>
????????{?',?'?}
????????<FilterLink?filter='SHOW_ISDO'>ISDO</FilterLink>
????</p>
)

export?{?AddTodo,?ShowTodoList,?Tab?}

?

入口文件 index.js

import?React?from?'react';
import?ReactDOM?from?'react-dom';
import?{?Provider?}?from?'react-redux';
import?{?createStore?}?from?'./redux';
import?todoList?from?'./reducers'
import?{AddTodo,?ShowTodoList,?Tab?}?from?'./component'

let?store?=?createStore(?todoApp?);

ReactDOM.render(
????<Provider?store={store}>
????????<div>
????????????<AddTodo?/>
????????????<ShowTodoList?/>
????????????<Tab?/>
????????</div>
????</Provider>
????,?document.getElementById('root'));

?

主要代碼完成,npm start 運行,功能截圖如下

Redux的核心概念,實現代碼與應用示例

文章同步發布:?https://www.geek-share.com/detail/2783420870.html

參考文章:

原生實現一個react-redux的代碼示例

用React實現一個完整的TodoList的示例代碼


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女