溫馨提示×

溫馨提示×

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

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

如何學(xué)習(xí)React-Hook

發(fā)布時(shí)間:2021-10-19 13:44:13 來源:億速云 閱讀:98 作者:iii 欄目:web開發(fā)

本篇內(nèi)容主要講解“如何學(xué)習(xí)React-Hook”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“如何學(xué)習(xí)React-Hook”吧!

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

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

在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ì)使用集合(對象)來處理一些邏輯。

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

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

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

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

  • 比如單個(gè) state 的狀態(tài)會(huì)影響多個(gè) state 的值的時(shí)候。

  • 比如多個(gè) state 的狀態(tài)會(huì)隨著某種類型的改變而改變。

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

const [state, dispatch] = React.useReducer(   (prevState, action) => {     switch (action.type) {       case "RESTORE_TOKEN":         return {           ...prevState,           userToken: action.token,           isLoading: false,         };       case "SIGN_IN":         return {           ...prevState,           isSignout: false,           userToken: action.token,         };       case "SIGN_OUT":         return {           ...prevState,           isSignout: true,           userToken: null,         };     }   },   {     isLoading: true,     isSignout: false,     userToken: null,   } );

副作用

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

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

它的作用有:

  • 它可以監(jiān)控 state 值的變化

  • 它可以處理只運(yùn)行一次的邏輯,有點(diǎn)類似生命周期 componentDidMount 和 componentWillUnmount 的思維模式,

  • 添加訂閱、設(shè)置定時(shí)器、發(fā)送網(wǎng)絡(luò)請求

  • 更多其他

// 通過的第二個(gè)參數(shù)來實(shí)現(xiàn)只執(zhí)行一次或監(jiān)控state值 useEffect(() => {   // ... }, []);  // useEffect第一個(gè)參數(shù)的返回函數(shù)就是componentWillUnmount的思想,在組件卸載之前進(jìn)行 useEffect(() => {   const subscription = props.source.subscribe();   return () => {     // 清除訂閱     subscription.unsubscribe();   }; });

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

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

所以useLayoutEffect的使用場景:

  • 防止閃爍,比較耗時(shí)的計(jì)算

  • Dom操作

  • componentDidMount和componentDidUpdate的場景

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

組件傳值

組件傳值的核心:

  • 父傳子,通過在子組件設(shè)置屬性;

  • 子傳父,通過回調(diào)。

  • 多級組件,通過中間狀態(tài)管理

// 父組件 function Home() {   const [currentTab, setCurrentTab] = useState("msg");   return (     <>       <View style={styles.logo}>          // 父傳子         <TabView currentTab={currentTab} setCurrentTab={setCurrentTab} />         // 子傳父         <CodeView code={code} changeCode={(code)=>setCurrentTab(code)} />       </View>       <Text>{currentTab}</Text>     </>   ); }  //子組件 function TabView({ currentTab, setCurrentTab }) {   return (     <View style={styles.logo}>       <Text>{currentTab}</Text>       <Button         title="修改tab"         onPress={() => {           setCurrentTab("pass");         }}       />     </View>   ); } //子傳父 function CodeView({ code, changeCode }) {   return (     <View style={styles.logo}>       <Text>{code}</Text>       <Button         title="修改tab"         onPress={changeCode}       />     </View>   ); }

多組件的傳值,可以通過context來處理。

export const MyContent = React.createContext({});  function Home() {   return (     <MyContent.Provider       value={{         currentTab,         phoneValue,         codeValue,         setPhoneValue,         setCodeValue,       }}     >       <FormItem />       <SwitchItemView />     </MyContent.Provider>   ); }  function FormItem() {   const { phoneValue, setPhoneValue } = useContext(MyContent);   return (     <View style={styles.logo}>       <Text>{phoneValue}</Text>       {/* ...*/}     </View>   ); }  function SwitchItemView() {   const { codeValue, setCodeValue } = useContext(MyContent);   return (     <View style={styles.logo}>       <Text>{phoneValue}</Text>       {/* ...*/}     </View>   ); }

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

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

function TextInputWithFocusButton() {   const inputEl = useRef(null);   const onButtonClick = () => {     inputEl.current.focus();   };   return (     <>       <input ref={inputEl} type="text" />       <button onClick={onButtonClick}>Focus the input</button>     </>   ); }

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

function FancyInput(props, pref) {   const inputRef = useRef();   useImperativeHandle(ref, () => ({     focus: () => {       inputRef.current.focus();     }   }));   return <input ref={inputRef} ... />; }  FancyInput = forwardRef(FancyInput);  <FancyInput ref={inputRef} />  // 父組件可以直接調(diào)用inputRef.current.focus()

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

function MeasureExample() {   const [height, setHeight] = useState(0);    const measuredRef = useCallback((node) => {     if (node !== null) {       setHeight(node.getBoundingClientRect().height);     }   }, []);    return (     <>       <h2 ref={measuredRef}>Hello, world</h2>       <h3>The above header is {Math.round(height)}px tall</h3>     </>   ); }

自定義hook

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

const useDebounce = (fn, ms = 30, deps = []) => {   let timeout = useRef();    useEffect(() => {     if (timeout.current) clearTimeout(timeout.current);     timeout.current = setTimeout(() => {       fn();     }, ms);   }, deps);    const cancel = () => {     clearTimeout(timeout.current);     timeout = null;   };   return [cancel]; };  export default useDebounce; const Home = (props) => {   const [a, setA] = useState(0);   const [b, setB] = useState(0);   const [cancel] = useDebounce(     () => {       setB(a);     },     2000,     [a]   );    const changeIpt = (e) => {     setA(e.target.value);   };    return (     <div>       <input type="text" onChange={changeIpt} />        {a}     </div>   ); };

性能優(yōu)化

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

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

  • 單個(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)。

const memoizedCallback = useCallback(() => {   doSomething(a, b); }, [a, b]);  // useMemo const [count, setCount] = useState(1); const [val, setValue] = useState(""); const expensive = useMemo(() => {   let sum = 0;   for (let i = 0; i < count * 100; i++) {     sum += i;   }   return sum; }, [count]);  return (   <div>     <h5>       {count}-{expensive}     </h5>     {val}     <div>       <button onClick={() => setCount(count + 1)}>+c1</button>       <input value={val} onChange={(event) => setValue(event.target.value)} />     </div>   </div> );

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

const memoize = function(fn) {     const cache = {};     return function() {         const key = JSON.stringify(arguments);         var value = cache[key];         if(!value) {             console.log('新值,執(zhí)行中...');         // 為了了解過程加入的log,正式場合應(yīng)該去掉             value = [fn.apply(this, arguments)];  // 放在一個(gè)數(shù)組中,方便應(yīng)對undefined,null等異常情況             cache[key] = value;         } else {             console.log('來自緩存');               // 為了了解過程加入的log,正式場合應(yīng)該去掉         }         return value[0];     } }  module.exports = memoize; const memoize = require('./memoize.js'); const log = console.log;  // 斐波那契數(shù)組 const fibonacci = (n) => {     return n < 2          ? n         : fibonacci(n - 1) + fibonacci(n - 2); };  const memoizeFibonacci = memoize(fibonacci);  log(memoizeFibonacci(45));   // 新值,執(zhí)行中...;    1134903170  // 等待時(shí)間比較長 log(memoizeFibonacci(45));   // 來自緩存;    1134903170 log(memoizeFibonacci(45));   // 來自緩存;    1134903170 log(memoizeFibonacci(45));   // 來自緩存;    1134903170

到此,相信大家對“如何學(xué)習(xí)React-Hook”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI