自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

學(xué)習(xí)React-Hook,我們應(yīng)該思考哪些東西

開發(fā) 前端
組合優(yōu)于繼承,我們一直在尋求「 解耦 」來(lái)把復(fù)雜的業(yè)務(wù)代碼簡(jiǎn)單化。而Hook的最大優(yōu)點(diǎn)就是代碼復(fù)用,更簡(jiǎn)潔。而且Hook在寫法上也遵循了「 單一職責(zé)模式 」。

 [[380371]]

組合優(yōu)于繼承,我們一直在尋求「 解耦 」來(lái)把復(fù)雜的業(yè)務(wù)代碼簡(jiǎn)單化。而Hook的最大優(yōu)點(diǎn)就是代碼復(fù)用,更簡(jiǎn)潔。而且Hook在寫法上也遵循了「 單一職責(zé)模式 」。

今天主要分享的是在學(xué)習(xí)Hook之前,我們應(yīng)該做哪些思考。

  • 數(shù)據(jù)綁定、狀態(tài)管理
  • 生命周期、組件掛載卸載
  • 組件傳值
  • 節(jié)點(diǎn)元素獲取
  • 復(fù)用邏輯抽離

數(shù)據(jù)綁定

在react中state的概念是內(nèi)部狀態(tài)的管理,它的變更直接會(huì)影響頁(yè)面的渲染。

在hook中把setState拆開了。每一個(gè)單個(gè)的state的都會(huì)是一個(gè)單個(gè)的狀態(tài),單個(gè)狀態(tài)的變更不會(huì)影響其他state。我們可以通過useState實(shí)現(xiàn)單個(gè)狀態(tài)的初始化定義。

useState的參數(shù)可以是一個(gè)數(shù)字、字符串、布爾值、數(shù)組或?qū)ο?,也可以是一個(gè)函數(shù)。

同樣我們避免不了會(huì)使用集合(對(duì)象)來(lái)處理一些邏輯。

  1. const [count, setCount] = useState(0); 
  2. const [count, setCount] = useState(() => 0); 
  3. const [obj, setObj] = useState({}); 
  4. setObj((prevObj) => { 
  5.   // 也可以使用 Object.assign 
  6.   return { ...prevObj, age: 23 }; 
  7. }); 

一般我們會(huì)定義一個(gè)初始值initialState,如果這個(gè)初始值,需要額外的計(jì)算開銷,我們可以定義一個(gè)函數(shù)來(lái)處理。需要注意的是,useState 函數(shù)只會(huì)在初始渲染的時(shí)候被調(diào)用。

  1. const [state, setState] = useState(() => { 
  2.   // 額外的操作someExpensiveComputation 
  3.   const initialState = someExpensiveComputation(props); 
  4.   return initialState; 
  5. }); 

對(duì)于多個(gè)state集合的處理,還有另一種方案就是useReducer。

  • 比如單個(gè) state 的狀態(tài)會(huì)影響多個(gè) state 的值的時(shí)候。
  • 比如多個(gè) state 的狀態(tài)會(huì)隨著某種類型的改變而改變。

如下多個(gè) state 會(huì)隨著登錄、登出、刷新 token 這三種狀態(tài)的改變而改變。

  1. const [state, dispatch] = React.useReducer( 
  2.   (prevState, action) => { 
  3.     switch (action.type) { 
  4.       case "RESTORE_TOKEN"
  5.         return { 
  6.           ...prevState, 
  7.           userToken: action.token, 
  8.           isLoading: false
  9.         }; 
  10.       case "SIGN_IN"
  11.         return { 
  12.           ...prevState, 
  13.           isSignout: false
  14.           userToken: action.token, 
  15.         }; 
  16.       case "SIGN_OUT"
  17.         return { 
  18.           ...prevState, 
  19.           isSignout: true
  20.           userToken: null
  21.         }; 
  22.     } 
  23.   }, 
  24.   { 
  25.     isLoading: true
  26.     isSignout: false
  27.     userToken: null
  28.   } 
  29. ); 

副作用

hook 提供了一種新的概念來(lái)代替生命周期函數(shù),就是useEffect副作用。它被看作是從 React 的純函數(shù)式世界通往命令式世界的逃生通道。

它的執(zhí)行時(shí)機(jī)是在屏幕元素渲染結(jié)束后延遲執(zhí)行。

它的作用有:

  • 它可以監(jiān)控 state 值的變化
  • 它可以處理只運(yùn)行一次的邏輯,有點(diǎn)類似生命周期 componentDidMount 和 componentWillUnmount 的思維模式,
  • 添加訂閱、設(shè)置定時(shí)器、發(fā)送網(wǎng)絡(luò)請(qǐng)求
  • 更多其他
  1. // 通過的第二個(gè)參數(shù)來(lái)實(shí)現(xiàn)只執(zhí)行一次或監(jiān)控state值 
  2. useEffect(() => { 
  3.   // ... 
  4. }, []); 
  5.  
  6. // useEffect第一個(gè)參數(shù)的返回函數(shù)就是componentWillUnmount的思想,在組件卸載之前進(jìn)行 
  7. useEffect(() => { 
  8.   const subscription = props.source.subscribe(); 
  9.   return () => { 
  10.     // 清除訂閱 
  11.     subscription.unsubscribe(); 
  12.   }; 
  13. }); 

但是這種延遲執(zhí)行的機(jī)制不能滿足我們所有的場(chǎng)景,如果我們想要實(shí)現(xiàn)屏幕繪制和副作用同步執(zhí)行,比如實(shí)時(shí)修改dom結(jié)構(gòu)等這樣的場(chǎng)景,useEffect無(wú)法滿足,會(huì)出現(xiàn)閃屏的效果。

我們可以通過useLayoutEffect來(lái)實(shí)現(xiàn),它的執(zhí)行時(shí)機(jī)是在組件加載完成后,屏幕繪制之前進(jìn)行。但這樣也有缺點(diǎn)就是阻塞屏幕渲染,可能會(huì)出現(xiàn)白屏或停頓。

所以u(píng)seLayoutEffect的使用場(chǎng)景:

  • 防止閃爍,比較耗時(shí)的計(jì)算
  • Dom操作
  • componentDidMount和componentDidUpdate的場(chǎng)景

如果只是單獨(dú)的獲取(get操作)就沒有必要使用useLayoutEffect。

組件傳值

組件傳值的核心:

  • 父?jìng)髯?,通過在子組件設(shè)置屬性;
  • 子傳父,通過回調(diào)。
  • 多級(jí)組件,通過中間狀態(tài)管理
  1. // 父組件 
  2. function Home() { 
  3.   const [currentTab, setCurrentTab] = useState("msg"); 
  4.   return ( 
  5.     <> 
  6.       <View style={styles.logo}> 
  7.          // 父?jìng)髯?nbsp;
  8.         <TabView currentTab={currentTab} setCurrentTab={setCurrentTab} /> 
  9.         // 子傳父 
  10.         <CodeView code={code} changeCode={(code)=>setCurrentTab(code)} /> 
  11.       </View
  12.       <Text>{currentTab}</Text> 
  13.     </> 
  14.   ); 
  15.  
  16. //子組件 
  17. function TabView({ currentTab, setCurrentTab }) { 
  18.   return ( 
  19.     <View style={styles.logo}> 
  20.       <Text>{currentTab}</Text> 
  21.       <Button 
  22.         title="修改tab" 
  23.         onPress={() => { 
  24.           setCurrentTab("pass"); 
  25.         }} 
  26.       /> 
  27.     </View
  28.   ); 
  29. //子傳父 
  30. function CodeView({ code, changeCode }) { 
  31.   return ( 
  32.     <View style={styles.logo}> 
  33.       <Text>{code}</Text> 
  34.       <Button 
  35.         title="修改tab" 
  36.         onPress={changeCode} 
  37.       /> 
  38.     </View
  39.   ); 

多組件的傳值,可以通過context來(lái)處理。 

  1. export const MyContent = React.createContext({}); 
  2.  
  3. function Home() { 
  4.   return ( 
  5.     <MyContent.Provider 
  6.       value={{ 
  7.         currentTab, 
  8.         phoneValue, 
  9.         codeValue, 
  10.         setPhoneValue, 
  11.         setCodeValue, 
  12.       }} 
  13.     > 
  14.       <FormItem /> 
  15.       <SwitchItemView /> 
  16.     </MyContent.Provider> 
  17.   ); 
  18.  
  19. function FormItem() { 
  20.   const { phoneValue, setPhoneValue } = useContext(MyContent); 
  21.   return ( 
  22.     <View style={styles.logo}> 
  23.       <Text>{phoneValue}</Text> 
  24.       {/* ...*/} 
  25.     </View
  26.   ); 
  27.  
  28. function SwitchItemView() { 
  29.   const { codeValue, setCodeValue } = useContext(MyContent); 
  30.   return ( 
  31.     <View style={styles.logo}> 
  32.       <Text>{phoneValue}</Text> 
  33.       {/* ...*/} 
  34.     </View
  35.   ); 

元素節(jié)點(diǎn)操作

hook通過useRef來(lái)創(chuàng)建節(jié)點(diǎn)對(duì)象,然后通過ref掛載,通過current來(lái)獲取。

  1. function TextInputWithFocusButton() { 
  2.   const inputEl = useRef(null); 
  3.   const onButtonClick = () => { 
  4.     inputEl.current.focus(); 
  5.   }; 
  6.   return ( 
  7.     <> 
  8.       <input ref={inputEl} type="text" /> 
  9.       <button onClick={onButtonClick}>Focus the input</button> 
  10.     </> 
  11.   ); 

我們可能會(huì)封裝一些邏輯,自定義一些屬性,暴露給父元素,那么我們就會(huì)用到useImperativeHandle和forwardRef。

  1. function FancyInput(props, pref) { 
  2.   const inputRef = useRef(); 
  3.   useImperativeHandle(ref, () => ({ 
  4.     focus: () => { 
  5.       inputRef.current.focus(); 
  6.     } 
  7.   })); 
  8.   return <input ref={inputRef} ... />; 
  9.  
  10. FancyInput = forwardRef(FancyInput); 
  11.  
  12. <FancyInput ref={inputRef} />  
  13. // 父組件可以直接調(diào)用inputRef.current.focus() 

因?yàn)?ref 對(duì)象不會(huì)把當(dāng)前 ref 值的變化通知給我們,所以我們必須通過useState和useCallback實(shí)現(xiàn)。

  1. function MeasureExample() { 
  2.   const [height, setHeight] = useState(0); 
  3.  
  4.   const measuredRef = useCallback((node) => { 
  5.     if (node !== null) { 
  6.       setHeight(node.getBoundingClientRect().height); 
  7.     } 
  8.   }, []); 
  9.  
  10.   return ( 
  11.     <> 
  12.       <h1 ref={measuredRef}>Hello, world</h1> 
  13.       <h2>The above header is {Math.round(height)}px tall</h2> 
  14.     </> 
  15.   ); 

自定義hook

在代碼中,我們會(huì)有一些共用的邏輯,我們可以抽離出來(lái)比如自定義的防抖節(jié)流,自定義 Hook 是一個(gè)函數(shù),其名稱以 “use” 開頭,函數(shù)內(nèi)部可以調(diào)用其他的 Hook。

  1. const useDebounce = (fn, ms = 30, deps = []) => { 
  2.   let timeout = useRef(); 
  3.  
  4.   useEffect(() => { 
  5.     if (timeout.current) clearTimeout(timeout.current); 
  6.     timeout.current = setTimeout(() => { 
  7.       fn(); 
  8.     }, ms); 
  9.   }, deps); 
  10.  
  11.   const cancel = () => { 
  12.     clearTimeout(timeout.current); 
  13.     timeout = null
  14.   }; 
  15.   return [cancel]; 
  16. }; 
  17.  
  18. export default useDebounce; 
  19. const Home = (props) => { 
  20.   const [a, setA] = useState(0); 
  21.   const [b, setB] = useState(0); 
  22.   const [cancel] = useDebounce( 
  23.     () => { 
  24.       setB(a); 
  25.     }, 
  26.     2000, 
  27.     [a] 
  28.   ); 
  29.  
  30.   const changeIpt = (e) => { 
  31.     setA(e.target.value); 
  32.   }; 
  33.  
  34.   return ( 
  35.     <div> 
  36.       <input type="text" onChange={changeIpt} /> 
  37.        {a} 
  38.     </div> 
  39.   ); 
  40. }; 

性能優(yōu)化

單向數(shù)據(jù)流,各組件層次分明,狀態(tài)明確,但是當(dāng)項(xiàng)目體量大,組件嵌套多的時(shí)候,性能損耗也非常大,所以我們會(huì)做一些性能優(yōu)化的工作。

可能的優(yōu)化場(chǎng)景有:

  • 單個(gè) state 的更新會(huì)影響全局,有一點(diǎn)需要注意的是,被context包裹的組件,只要value的任何一個(gè)變動(dòng),都會(huì)重新渲染,useMemo和useCallback就會(huì)失效。
  • 相同的輸入,不再重新計(jì)算
  • 父組件的函數(shù)在子組件使用的時(shí)候

其實(shí)useMemo和useCallback的核心思想相同,都是記錄上一次的輸入,如果下一次輸入與上一次相同,將不會(huì)計(jì)算,直接獲取上一次的結(jié)果。他們的區(qū)別只是形式上的,useMemo返回一個(gè) memoized 值。而useCallback返回的是memoized回調(diào)函數(shù)。

useMemo緩存計(jì)算結(jié)果的值。

useCallback主要用于緩存函數(shù)。

useCallback(fn, deps) 相當(dāng)于 useMemo(() => fn, deps)。

  1. const memoizedCallback = useCallback(() => { 
  2.   doSomething(a, b); 
  3. }, [a, b]); 
  4.  
  5. // useMemo 
  6. const [count, setCount] = useState(1); 
  7. const [val, setValue] = useState(""); 
  8. const expensive = useMemo(() => { 
  9.   let sum = 0; 
  10.   for (let i = 0; i < count * 100; i++) { 
  11.     sum += i; 
  12.   } 
  13.   return sum
  14. }, [count]); 
  15.  
  16. return ( 
  17.   <div> 
  18.     <h4> 
  19.       {count}-{expensive} 
  20.     </h4> 
  21.     {val} 
  22.     <div> 
  23.       <button onClick={() => setCount(count + 1)}>+c1</button> 
  24.       <input value={val} onChange={(event) => setValue(event.target.value)} /> 
  25.     </div> 
  26.   </div> 
  27. ); 

捎帶了解一下memoized,簡(jiǎn)單講就是把函數(shù)的計(jì)算結(jié)果緩存起來(lái),比如遞歸。

  1. const memoize = function(fn) { 
  2.     const cache = {}; 
  3.     return function() { 
  4.         const key = JSON.stringify(arguments); 
  5.         var value = cache[key]; 
  6.         if(!value) { 
  7.             console.log('新值,執(zhí)行中...');         // 為了了解過程加入的log,正式場(chǎng)合應(yīng)該去掉 
  8.             value = [fn.apply(this, arguments)];  // 放在一個(gè)數(shù)組中,方便應(yīng)對(duì)undefined,null等異常情況 
  9.             cache[key] = value; 
  10.         } else { 
  11.             console.log('來(lái)自緩存');               // 為了了解過程加入的log,正式場(chǎng)合應(yīng)該去掉 
  12.         } 
  13.         return value[0]; 
  14.     } 
  15.  
  16. module.exports = memoize; 
  17. const memoize = require('./memoize.js'); 
  18. const log = console.log; 
  19.  
  20. // 斐波那契數(shù)組 
  21. const fibonacci = (n) => { 
  22.     return n < 2  
  23.         ? n 
  24.         : fibonacci(n - 1) + fibonacci(n - 2); 
  25. }; 
  26.  
  27. const memoizeFibonacci = memoize(fibonacci); 
  28.  
  29. log(memoizeFibonacci(45));   // 新值,執(zhí)行中...;    1134903170  // 等待時(shí)間比較長(zhǎng) 
  30. log(memoizeFibonacci(45));   // 來(lái)自緩存;    1134903170 
  31. log(memoizeFibonacci(45));   // 來(lái)自緩存;    1134903170 
  32. log(memoizeFibonacci(45));   // 來(lái)自緩存;    1134903170 

本文轉(zhuǎn)載自微信公眾號(hào)「 驚天碼盜」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 驚天碼盜公眾號(hào)。

責(zé)任編輯:武曉燕 來(lái)源: 驚天碼盜
相關(guān)推薦

2011-06-16 20:05:41

SEO

2017-11-30 10:55:16

科技法律換頭術(shù)

2024-07-16 09:51:39

HTMLHookReact

2022-04-14 11:50:39

函數(shù)組件hook

2017-09-11 15:46:36

數(shù)據(jù)科學(xué)語(yǔ)言Java

2016-12-06 16:51:11

互聯(lián)網(wǎng)時(shí)代IT技術(shù)

2022-08-01 07:56:23

React Hook開發(fā)組件

2020-10-21 11:34:49

React Hook庫(kù)

2024-01-09 07:26:16

ReactVue前端

2020-05-28 13:33:30

React Hook前端開發(fā)

2022-05-06 07:31:01

useEventReactHook

2017-09-28 11:54:11

PHP

2021-03-30 11:29:02

人工智能深度學(xué)習(xí)

2022-08-02 15:18:00

React開源項(xiàng)目

2017-10-24 14:21:30

機(jī)器學(xué)習(xí)人工智能算法

2017-06-27 14:49:20

深度學(xué)習(xí)機(jī)器學(xué)習(xí)

2018-06-15 22:41:06

開源軟件React軟件開發(fā)

2014-05-14 10:09:07

2015-08-05 17:16:17

電影ip像素游戲像素大戰(zhàn)

2021-05-14 09:49:47

React HookReact應(yīng)用
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)