react dispatch_React系列自定義Hooks很簡單

React系列-Mixin、HOC、Render Props(上)

React系列-輕松學會Hooks(中)

React系列-自定義Hooks很簡單(下)

我們在第二篇文章中介紹了一些常用的hooks,接著我們繼續來介紹剩下的hooks吧

useReducer

作為useState 的替代方案。它接收一個形如(state, action) => newState 的 reducer,并返回當前的 state 以及與其配套的 dispatch 方法。(如果你熟悉 Redux 的話,就已經知道它如何工作了。)

不明白Redux工作流的同學可以看看這篇Redux系列之分析中間件原理(附經驗分享)

為什么使用

官方說法: 在某些場景下,useReducer 會比 useState 更適用,例如 state 邏輯較復雜且包含多個子值,或者下一個 state 依賴于之前的 state 等。并且,使用 useReducer 還能給那些會觸發深更新的組件做性能優化,因為你可以向子組件傳遞 dispatch 而不是回調函數 。

總結來說:

  • 如果你的state是一個數組或者對象等復雜數據結構

  • 如果你的state變化很復雜,經常一個操作需要修改很多state

  • 如果你希望構建自動化測試用例來保證程序的穩定性

  • 如果你需要在深層子組件里面去修改一些狀態(也就是useReducer+useContext代替Redux)

  • 如果你用應用程序比較大,希望UI和業務能夠分開維護

登錄場景

舉個例子?:

登錄場景

useState完成登錄場景

????function?LoginPage()?{
????????const?[name,?setName]?=?useState('');?//?用戶名
????????const?[pwd,?setPwd]?=?useState('');?//?密碼
????????const?[isLoading,?setIsLoading]?=?useState(false);?//?是否展示loading,發送請求中
????????const?[error,?setError]?=?useState('');?//?錯誤信息
????????const?[isLoggedIn,?setIsLoggedIn]?=?useState(false);?//?是否登錄

????????const?login?=?(event)?=>?{
????????????event.preventDefault();
????????????setError('');
????????????setIsLoading(true);
????????????login({?name,?pwd?})
????????????????.then(()?=>?{
????????????????????setIsLoggedIn(true);
????????????????????setIsLoading(false);
????????????????})
????????????????.catch((error)?=>?{
????????????????????//?登錄失敗:?顯示錯誤信息、清空輸入框用戶名、密碼、清除loading標識
????????????????????setError(error.message);
????????????????????setName('');
????????????????????setPwd('');
????????????????????setIsLoading(false);
????????????????});
????????}
????????return?(?
????????????//??返回頁面JSX?Element
????????)
????}

useReducer完成登錄場景


????const?initState?=?{
????????name:?'',
????????pwd:?'',
????????isLoading:?false,
????????error:?'',
????????isLoggedIn:?false,
????}
????function?loginReducer(state,?action)?{
????????switch(action.type)?{
????????????case?'login':
????????????????return?{
????????????????????...state,
????????????????????isLoading:?true,
????????????????????error:?'',
????????????????}
????????????case?'success':
????????????????return?{
????????????????????...state,
????????????????????isLoggedIn:?true,
????????????????????isLoading:?false,
????????????????}
????????????case?'error':
????????????????return?{
????????????????????...state,
????????????????????error:?action.payload.error,
????????????????????name:?'',
????????????????????pwd:?'',
????????????????????isLoading:?false,
????????????????}
????????????default:?
????????????????return?state;
????????}
????}
????function?LoginPage()?{
????????const?[state,?dispatch]?=?useReducer(loginReducer,?initState);
????????const?{?name,?pwd,?isLoading,?error,?isLoggedIn?}?=?state;
????????const?login?=?(event)?=>?{
????????????event.preventDefault();
????????????dispatch({?type:?'login'?});
????????????login({?name,?pwd?})
????????????????.then(()?=>?{
????????????????????dispatch({?type:?'success'?});
????????????????})
????????????????.catch((error)?=>?{
????????????????????dispatch({
????????????????????????type:?'error'
????????????????????????payload:?{?error:?error.message?}
????????????????????});
????????????????});
????????}
????????return?(?
????????????//??返回頁面JSX?Element
????????)
????}

??我們的state變化很復雜,經常一個操作需要修改很多state,另一個好處是所有的state處理都集中到了一起,使得我們對state的變化更有掌控力,同時也更容易復用state邏輯變化代碼,比如在其他函數中也需要觸發登錄success狀態,只需要dispatch({ type: 'success' })。

筆者[狗頭]認為,暫時應該不會用useReducer替代useState,畢竟Redux的寫法實在是很繁瑣

復雜數據結構場景

剛好最近筆者的項目就碰到了復雜數據結構場景,可是并沒有用useReducer來解決,依舊采用useState,原因很簡單:方便

//?定義list類型
??export?interface?IDictList?extends?IList?{
??extChild:?{
????curPage:?number
????totalSize:?number
????size:?number?//?pageSize
????list:?IList[]
???}
?}
?const?[list,?setList]?=?useState([])const?change=()=>{const?datalist?=?JSON.parse(JSON.stringify(list))?//?拷貝對象?地址不同?不過這種寫法感覺不好?建議用reducers?應該封裝下reducers寫法const?data?=?await?getData()const?{?totalCount,?pageSize,?list?}?=?data
??????item.extChild.totalSize?=?totalCount
??????item.extChild.size?=?pageSize
??????item.extChild.list?=?list
??????setList(datalist)?//?改變
?}

看typescript寫的類型聲明就知道了這個list變量是個復雜的數據結構,需要經常需要改變添加extChild.list數組的內容,但是這種Array.prototype.push,是不會觸發更新,做過是通過const datalist = JSON.parse(JSON.stringify(list))。雖然沒有使用useReducer進行替代,筆者還是推薦大家試試

如何使用

const?[state,?dispatch]?=?useReducer(reducer,?initialArg,?init);

知識點合集

引用不變

useReducer返回的state跟ref一樣,引用是不變的,不會隨著函數組件的重新更新而變化,因此useReducer也可以解決閉包陷阱

const?setCountReducer?=?(state,action)=>{
??switch(action.type){
????case?'add':
??????return?state+action.value
????case?'minus':
??????return?state-action.value
????default:
??????return?state
??}
}

const?App?=?()=>{
??const?[count,dispatch]?=?useReducer(setCountReducer,0)
??useEffect(()=>{
????const?timeId?=?setInterval(()=>{
??????dispatch({type:'add',value:1})
????},1000)
????return?()=>?clearInterval(timeId)
??},[])
??return?(
????<span>{count}span>
??)
}

把setCount改成useReducer的dispatch,因為useReducer的dispatch 的身份永遠是穩定的 —— 即使 reducer 函數是定義在組件內部并且依賴 props

useContext

,useContext肯定與React.createContext有關系的,接收一個 context 對象(React.createContext 的返回值)并返回該 context 的當前值。當前的 context 值由上層組件中距離當前組件最近的 的 value prop 決定。

為什么使用

如果你在接觸 Hook 前已經對 context API 比較熟悉,那應該可以理解,useContext(MyContext) 相當于 class 組件中的 static contextType = MyContext 或者 。簡單點說就是useContext是用來消費context API

如何使用

const?value?=?useContext(MyContext);

知識點合集

useContext造成React.memo 無效

當組件上層最近的 更新時,該 Hook 會觸發重渲染,并使用最新傳遞給 MyContext provider 的 context value 值。即使祖先使用 React.memo 或 shouldComponentUpdate??也會在組件本身使用 useContext 時重新渲染

舉個例子?:

//?創建一個?context
const?Context?=?React.createContext()
//?用memo包裹
const?Item?=?React.memo((props)?=>?{
??//?組件一,?useContext?寫法
??const?count?=?useContext(Context);
??console.log('props',?props)
??return?(
????<div>{count}div>
??)
})

const?App?=?()?=>?{
??const?[count,?setCount]?=?useState(0)
??return?(
????<div>
??????點擊次數:?{?count}<button?onClick={()?=>?{?setCount(count?+?1)?}}>點我button><Context.Provider?value={count}><Item?/>Context.Provider>div>
??)
}

結果:

8117dddb59bca5553fc3c5c960ec5845.png

可以看到即使props沒有變化,一旦組件上層最近的 更新時,該 Hook 會觸發重渲染,此時Memo就失效了

Hooks替代Redux

有了useReduceruseContext以及React.createContext API,我們可以實現自己的狀態管理來替換Redux

實現react-redux

react-redux:React Redux is the official React binding for Redux. It lets your React components read data from a Redux store, and dispatch actions to the store to update data.

簡單理解就是連接組件和數據中心,也就是把React和Redux聯系起來,可以看看官方文檔或者看看阮一峰老師的文章,這里我們要去實現它最主要的兩個API

Provider 組件

Provider:組件之間共享的數據是 Provider 這個頂層組件通過 props 傳遞下去的,store必須作為參數放到Provider組件中去

利用React.createContext這個API,實現起來非常easy,react-redux本身就是依賴這個API的

const?MyContext?=?React.createContext()

const?MyProvider?=?MyContext.Provider

export?default?MyProvider?//?導出

connect

connect:connect是一個高階組件,提供了一個連接功能,可用于將組件連接到store,它 提供了組件獲取 store 中數據或者更新數據的接口(mapStateToProps和mapStateToProps)的能力

connect([mapStateToProps], [mapStateToProps], [mergeProps], [options])

function?connect(mapStateToProps,?mapDispatchToProps)?{
????return?function?(Component)?{
????????return?function?()?{
????????????const?{state,?dispatch}?=?useContext(MyContext)
????????????const?stateToProps?=?mapStateToProps(state)
????????????const?dispatchToProps?=?mapDispatchToProps(dispatch)
????????????const?props?=?{...props,?...stateToProps,?...dispatchToProps}
????????????return?(
????????????????<Component?{...props}?/>
????????????)
????????}
????}
}
export?default?connect?//?導出

創建store

store: store對象包含所有數據。如果想得到某個時點的數據,就要對 Store 生成快照。這種時點的數據集合,就叫做 State。

利用useReducer來創建我們的store


?import?React,?{?Component,?useReducer,?useContext?}?from?'react';
import?{?render?}?from?'react-dom';
import?'./style.css';

const?MyContext?=?React.createContext()
const?MyProvider?=?MyContext.Provider;

function?connect(mapStateToProps,?mapDispatchToProps)?{
????return?function?(Component)?{
????????return?function?()?{
????????????const?{state,?dispatch}?=?useContext(MyContext)
????????????const?stateToProps?=?mapStateToProps(state)
????????????const?dispatchToProps?=?mapDispatchToProps(dispatch)
????????????const?props?=?{...props,?...stateToProps,?...dispatchToProps}

????????????return?(
????????????????<Component?{...props}?/>
????????????)
????????}
????}
}
function?FirstC(props)?{
????console.log("FirstC更新")
????return?(<div><h2>這是FirstCh2><h3>{props.books}h3><button?onClick={()=>?props.dispatchAddBook("Dan?Brown:?Origin")}>Dispatch?'Origin'button>div>
????)
}
function?mapStateToProps(state)?{
????return?{
????????books:?state.Books
????}
}
function?mapDispatchToProps(dispatch)?{
????return?{
????????dispatchAddBook:?(payload)=>?dispatch({type:?'ADD_BOOK',?payload})
????}
}
const?HFirstC?=?connect(mapStateToProps,?mapDispatchToProps)(FirstC)
function?SecondC(props)?{
???console.log("SecondC更新")
????return?(<div><h2>這是SecondCh2><h3>{props.books}h3><button?onClick={()=>?props.dispatchAddBook("Dan?Brown:?The?Lost?Symbol")}>Dispatch?'The?Lost?Symbol'button>div>
????)
}
function?_mapStateToProps(state)?{
????return?{
????????books:?state.Books
????}
}
function?_mapDispatchToProps(dispatch)?{
????return?{
????????dispatchAddBook:?(payload)=>?dispatch({type:?'ADD_BOOK',?payload})
????}
}
const?HSecondC?=?connect(_mapStateToProps,?_mapDispatchToProps)(SecondC)
function?App?()?{
????const?initialState?=?{
??????Books:?'Dan?Brown:?Inferno'
????}
????const?[state,?dispatch]?=?useReducer((state,?action)?=>?{
??????switch(action.type)?{
????????case?'ADD_BOOK':
??????????return?{?Books:?action.payload?}
????????default:
??????????return?state
??????}
????},?initialState);
????return?(<div><MyProvider?value={{state,?dispatch}}><HFirstC?/><HSecondC?/>MyProvider>div>
????)
}
render(<App?/>,?document.getElementById('root'));

結果:

0ce22f648cb0aed4cd7144723cf14b2c.png

嗯嗯?,我們就這樣實現了一個狀態管理

缺陷

  • 缺少時間旅行
  • 不支持中間件
  • 性能極差

可以看到上面的結果,一個狀態變化,所有組件都重新渲染,嗯嗯?,所以我們這是個demo玩玩而已,不要用于生產中

最后貼下Redux作者的回答:

9eea6ecc9f4f133b17dceb8ba0d84e5b.png

useLayoutEffect

useLayoutEffect和useEffect一樣也是處理副作用,其函數簽名與 useEffect 相同,但它會在所有的 DOM 變更之后同步調用 effect。可以使用它來讀取 DOM 布局并同步觸發重渲染。在瀏覽器執行繪制之前,useLayoutEffect 內部的更新計劃將被同步刷新。

??官方盡量推薦使用useEffect,因為useLayoutEffect,useLayoutEffect里面的callback函數會在DOM更新完成后立即執行,但是會在瀏覽器進行任何繪制之前運行完成,阻塞了瀏覽器的繪制

區別就是:useEffect是異步的,useLayoutEffect是同步的

為什么使用

解決一些閃爍場景

如何使用


useLayoutEffect(fn,?[])?//?接收兩個參數?一個是回調函數?另外一個是數組類型的參數(表示依賴)

知識點合集

??暫無...

自定義hooks

自定義Hooks很簡單,利用官方提供的Hook我們可以把重用的邏輯抽離出來,也就是我們的自定義Hook,當你在一個項目中發現大量類似代碼,那就抽離成Hooks吧

??前面我們分析了Mixin,HOC,Render Props這些模式來實現狀態邏輯復用,這里的自定義hooks也是解決狀態邏輯復用問題的一種模式(?終于快完結了)

根據業務來說,我把自定義Hooks分為兩類,一類是自定義基礎Hooks,另一類是自定義業務Hooks

業務Hooks

比如我們多個頁面有相同的請求方法

//?以use開頭
export?const?useUserData?=?(category:?string[],?labelName?:?string)?=>?{
??const?[baseTotal,?setBaseTotal]?=?useState(0)
??useEffect(()?=>?{
????dealSearchTotal(category,?labelName)
??},?[labelName,?category])const?dealSearchTotal?=?async?(
??)?=>?{const?data?=?await?getUserData(curCategory,?labelName)const?{?baseTotal,?calculateTotal,?basicTotal,?extTotal?}?=?data
????setBaseTotal(baseTotal)
??}//?最后return出想要的數據return?[baseTotal,?calculateTotal,?basicTotal,?extTotal]
}

??好如果你注意到你寫了重復代碼,抽離成自定義Hooks是沒問題的

基礎Hooks

基礎Hooks就是平時與業務無關的工具方法

useEffectOnce

該Hooks在函數組件只執行一次

const?useEffectOnce?=?(effect)?=>?{
??useEffect(effect,?[]);
};

export?default?useEffectOnce;

useMount

該Hook在組件掛載時調用

const?useMount?=?(fn)?=>?{
??useEffectOnce(()?=>?{
????fn();
??});
};

export?default?useMount;

useUnmount

該Hook在組件銷毀時調用

const?useUnmount?=?(fn:?()?=>?any):?void?=>?{
??const?fnRef?=?useRef(fn);
??fnRef.current?=?fn;
??useEffectOnce(()?=>?()?=>?fnRef.current());
};

export?default?useUnmount;

usePrevious

獲取組件的state或者props的舊值

const?usePrevious?=?(state):?=>?{
??const?ref?=?useRef();
??useEffect(()?=>?{
????ref.current?=?state;
??});
??return?ref.current;
};
export?default?usePrevious;

??其它參考Umi Hooks

最后

b121e3b5e99442527374ac3f4b0ecaa0.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/458000.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/458000.shtml
英文地址,請注明出處:http://en.pswp.cn/news/458000.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

三大UML建模工具Visio、Rational Rose、PowerDesign的區別

本文源自http://developer.51cto.com/art/201006/207993.htm UML建模工具Visio 、Rational Rose、PowerDesign的比較 ROSE是直接從UML發展而誕生的設計工具&#xff0c;它的出現就是為了對UML建模的支持&#xff0c;ROSE一開始沒有對數據庫端建模的支持&#xff0c;但是在現在…

內核中的內存申請:kmalloc、vmalloc、kzalloc、get_free_pages 之間的區別

kmalloc vmalloc kzalloc get_free_page()是內核空間申請內存空間函數 malloc是用戶空間申請內存函數 一 &#xff0c;kmalloc() 與 kfree() 和get_free_page的區別 1,用于申請較小的、連續的物理內存&#xff1a;使用的是內存分配器slab一小片。申請的內存位于物理內存…

前端 保存后端傳來數據的id_一篇來自前端同學對后端接口的吐槽

前言去年的某個時候就想寫一篇關于接口的吐槽&#xff0c;當時后端提出了接口方案對于我來說調用起來非常難受&#xff0c;但又說不上為什么&#xff0c;沒有論點論據所以也就作罷。最近因為寫全棧的緣故&#xff0c;團隊內部也遇到了一些關于接口設計的問題&#xff0c;于是開…

2018-2019-1 《信息安全系統設計基礎》教學進程

《信息安全系統設計基礎》教學進程 目錄 考核方式暑假準備教學進程 第01周學習任務和要求第02周學習任務和要求第03周學習任務和要求第04周學習任務和要求第05周學習任務和要求第06周學習任務和要求第07周學習任務和要求第08周學習任務和要求第09周學習任務和要求第10周學習任務…

python字符串是什么_python字符串詳解

字符串詳解 在python中引號引起來的就是字符串 字符串是用來存儲少量數據 索引 索引(下標) 通過索引可以精確的定位到某個元素 name "meat" meat 每一個字母叫做一個元素 # 0123 從左向右代表每一個字母 #-4-3-2-1 從右向左每一個字母 print(name[-1]) 輸出 t name …

最小編輯代價

最小編輯代價問題&#xff1a; 對于兩個字符串A和B&#xff0c;我們需要進行插入、刪除和修改操作將A串變為B串&#xff0c;定義c0&#xff0c;c1&#xff0c;c2分別為三種操作的代價&#xff0c;請設計一個高效算法&#xff0c;求出將A串變為B串所需要的最少代價。 給定兩個字…

Android中的數據庫

2019獨角獸企業重金招聘Python工程師標準>>> 1.1. 什么時候使用數據庫 有大量相似結構的數據需要存儲的時候就可以使用數據庫。 1.2. SQLite的簡介 SQLite是一款輕量級的數據庫。它的設計目標是嵌入式的&#xff0c;而且目前已經在很多嵌入式產品中使用了它。Androi…

python計算績效工資_python實現 --工資管理系統

原博文 2017-07-25 22:41 ? # -*- coding: utf-8 -*- __author__ hjianli # import re import os info_message """Alex 100000 Rain 80000 Egon 50000 Yuan 30000 """ #序列字典 xulie_...01669 相關推薦 2019-09-28 21:13 ? Python python…

為Windows Server 2012 R2指定授權服務器

為Windows Server 2012 R2指定授權服務器在Windows Server 2008 R2的終端服務中&#xff0c;可以手動指定授權服務器&#xff0c;而在Windows Server 2012 R2中&#xff0c;默認只能通過"遠程桌面連接服務"管理器&#xff0c;指定授權服務器&#xff0c;而要使用遠程…

spring5高級編程_Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新

簡介是什么讓java世界變得更好&#xff0c;程序員變得更友愛&#xff0c;禿頭率變得不是那么的高&#xff0c;讓程序員不必再每天996&#xff0c;有時間找個女朋友&#xff1f;是Spring。是什么讓企業級java應用變得簡單易懂&#xff0c;降低了java程序員的進入門檻&#xff0c…

關于resolve非泛型方法不能與類型實參一起使用

今天mvc新建三層時&#xff0c;寫到bll層中一直報下面的錯誤&#xff0c;檢查了幾遍趕腳并沒有什么錯。最后發現缺少一些引用。 如下面的圖&#xff0c;少添加了下面的兩個引用.Unity是微軟模式與實踐團隊開發的一個輕量級、可擴展的依賴注入容器, Microsoft.Practices.Unity.C…

設計模式-Singleton

2019獨角獸企業重金招聘Python工程師標準>>> Singleton算是知道的設計模式中最簡單的最方便實現的了&#xff0c;模式實現了對于類提供唯一實例的方法&#xff0c;在很多系統中都會用到此模式。在實際項目中使用全局變量&#xff0c;或者靜態函數等方式也可以達到這…

dump分析工具_Java應用CPU過高,如何排查?參考解決思路和常用工具總結

本文總結了一些常見的線上應急現象和對應排查步驟和工具。分享的主要目的是想讓對線上問題接觸少的同學有個預先認知&#xff0c;免得在遇到實際問題時手忙腳亂。畢竟作者自己也是從手忙腳亂時走過來的。只不過這里先提示一下。在線上應急過程中要記住&#xff0c;只有一個總體…

st官網下載stm32固件庫方法

進入www.st.com官網------把網站改成中文&#xff08;就在右上方&#xff09;----點擊產品-----選擇右側的微控制器選項------選擇左側的STM32 32位ARM CortexMCU-----選擇左側的STM32F1系列-----選擇STM32103-----選擇中間部分mcu對應型號&#xff08;我用的是STM32F103ZE)---…

mysql5.5提示Deprecated: mysql_query(): The mysql extension is deprecated

解決方法1&#xff1a;在php程序代碼里面設置報警級別 <?php error_reporting E_ALL & ~E_DEPRECATED 方法2&#xff1a;禁止php報錯 display_errors On 改為 display_errors Off 方法3&#xff1a;使用mysqli或者PDO 建議大家盡快取消mysql&#xff0c;全部都走…

JavaScript強化教程 —— Cocos2d-JS極速調試技巧

本文為 H5EDU 機構官方 HTML5培訓 教程&#xff0c;主要介紹&#xff1a;JavaScript強化教程 —— Cocos2d-JS極速調試技巧 本文教大家一個調試Cocos2d-JS的小技巧&#xff0c;我都是這么用的&#xff0c;特意來告訴大家這個輕量快速的調試技巧。1.首先我們需要安裝官方的cocos…

dos攻擊命令_Kali Linux系列之拒絕服務攻擊(DOS)實戰(上)

(你的世界是個什么樣的世界&#xff1f;你說&#xff0c;我們傾聽!)-----------------小百科拒絕服務攻擊即是攻擊者想辦法讓目標機器停止提供服務&#xff0c;是黑客常用的攻擊手段之一。其實對網絡帶寬進行的消耗性攻擊只是拒絕服務攻擊的一小部分&#xff0c;只要能夠對目標…

stm32定時器配置

stm32通用定時器 STM32的定時器是個強大的模塊&#xff0c;定時器使用的頻率也是很高的&#xff0c;定時器可以做一些基本的定時&#xff0c;還可以做PWM輸出或者輸入捕獲功能。 時鐘源問題&#xff1a; 名為TIMx的有八個&#xff0c;其中TIM1和TIM8掛在APB2總線上&#xff0c;…

SQL 養成一個好習慣是一筆財富

來源&#xff1a;MR_ke 鏈接&#xff1a;http://www.cnblogs.com/MR_ke/archive/2011/05/29/2062085.html 我們做軟件開發的&#xff0c;大部分人都離不開跟數據庫打交道&#xff0c;特別是erp開發的&#xff0c;跟數據庫打交道更是頻繁&#xff0c;存儲過程動不動就是上千行&a…

【JAVA】StringTokenizer 迭代方式對字符串進行分割

StringTokenizer是一個用來分隔String的應用類&#xff0c;相當于VB的split函數。1.構造函數public StringTokenizer(String str)public StringTokenizer(String str, String delim)public StringTokenizer(String str, String delim, boolean returnDelims)第一個參數就是要分…