useReducer-基礎概念篇html
useReducer-配合useContext使用react
useReducer
是React提供的一個高級Hook,它不像useEffect、useState、useRef等必須hook同樣,沒有它咱們也能夠正常完成需求的開發,但useReducer可使咱們的代碼具備更好的可讀性、可維護性、可預測性。git
下面咱們會分三篇文章詳細介紹如何在項目中使用useReducer
:github
reducer
的概念以及它的特色,對reducer、redux等比較熟悉的小夥伴能夠跳過本篇useReducer
的使用方式和它的場景,以及useReducer帶來的好處reducer
的概念是伴隨着Redux的出現逐漸在JavaScript中流行起來。但咱們並不須要學習Redux去了解Reducer。簡單來講 reducer是一個函數(state, action) => newState
:接收當前應用的state和觸發的動做action,計算並返回最新的state。下面是一段僞代碼:redux
// 舉個栗子 計算器reducer,根據state(當前狀態)和action(觸發的動做加、減)參數,計算返回newState function countReducer(state, action) { switch(action.type) { case 'add': return state + 1; case 'sub': return state - 1; default: return state; } } 複製代碼
上面例子:state是一個number類型的數值,reducer根據action的類型(加、減)對應的修改state,並返回最終的state。爲了剛接觸到reducer
概念的小夥伴更容易理解,能夠將state改成count,但請始終牢記count仍然是state。markdown
function countReducer(count, action) { switch(action.type) { case 'add': return count + 1; case 'sub': return count - 1; default: return count; } } 複製代碼
從上面的示例能夠看到reducer
本質是一個純函數,沒有任何UI和反作用。這意味着相同的輸入(state、action),reducer函數不管執行多少遍始終會返回相同的輸出(newState)。所以經過reducer函數很容易推測state的變化,而且也更加容易單元測試。app
expect(countReducer(1, { type: 'add' })).equal(2); // 成功 expect(countReducer(1, { type: 'add' })).equal(2); // 成功 expect(countReducer(1, { type: 'sub' })).equal(0); // 成功 複製代碼
state
是當前應用狀態對象,能夠理解就是咱們熟知的React裏面的state。函數
在上面的例子中state是一個基礎數據類型,但不少時候state可能會是一個複雜的JavaScript對象,如上例中count有可能只是 state中的一個屬性。針對這種場景咱們可使用ES6的結構賦值:oop
// 返回一個 newState (newObject) function countReducer(state, action) { switch(action.type) { case 'add': return { ...state, count: state.count + 1; } case 'sub': return { ...state, count: state.count - 1; } default: return count; } } 複製代碼
關於上面這段代碼有兩個重要的點須要咱們記住:
reducer處理的state對象必須是immutable
,這意味着永遠不要直接修改參數中的state對象,reducer函數應該每次都返回一個新的state object
既然reducer要求每次都返回一個新的對象,咱們可使用ES6中的解構賦值方式去建立一個新對象,並複寫咱們須要改變的state屬性,如上例。
看上去很完美,但若是咱們的state是多層嵌套,解構賦值實現就很是複雜:
function bookReducer(state, action) { switch(action.type) { // 添加一本書 case 'addBook': return { ...state, books: { ...state.books, [bookId]: book, } }; case 'sub': // .... default: return state; } } 複製代碼
對於這種複雜state的場景推薦使用immer等immutable庫解決。
咱們上文提到過reducer須要保持冪等性,更加可預測、可測試。若是每次返回同一個state,就沒法保證不管執行多少次都是相同的結果
React在比較oldState
和newState
的時候是使用Object.is函數,若是是同一個對象則不會出發組件的rerender。 能夠參考官方文檔bailing-out-of-a-dispatch。
action:用來表示觸發的行爲。
const action = { type: 'addBook', payload: { book: { bookId, bookName, author, } } } function bookReducer(state, action) { switch(action.type) { // 添加一本書 case 'addBook': const { book } = action.payload; return { ...state, books: { ...state.books, [book.bookId]: book, } }; case 'sub': // .... default: return state; } } 複製代碼
至此基本介紹完了reducer相關的內容,簡單總結一下:reducer
是一個利用action
提供的信息,將state
從A轉換到B的一個純函數,具備一下幾個特色:
下篇文章咱們會進入正題:如何使用useReducer簡化咱們的state管理。
最後慣例,歡迎你們star咱們的人人貸大前端團隊博客,全部的文章還會同步更新到知乎專欄 和 掘金帳號,咱們每週都會分享幾篇高質量的大前端技術文章。若是你喜歡這篇文章,但願能動動小手給個贊。