您好,登錄后才能下訂單哦!
本篇內容主要講解“如何學習React-Hook”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“如何學習React-Hook”吧!
數據綁定
在react中state的概念是內部狀態的管理,它的變更直接會影響頁面的渲染。
在hook中把setState拆開了。每一個單個的state的都會是一個單個的狀態,單個狀態的變更不會影響其他state。我們可以通過useState實現單個狀態的初始化定義。
useState的參數可以是一個數字、字符串、布爾值、數組或對象,也可以是一個函數。
同樣我們避免不了會使用集合(對象)來處理一些邏輯。
const [count, setCount] = useState(0); const [count, setCount] = useState(() => 0); const [obj, setObj] = useState({}); setObj((prevObj) => { // 也可以使用 Object.assign return { ...prevObj, age: 23 }; });
一般我們會定義一個初始值initialState,如果這個初始值,需要額外的計算開銷,我們可以定義一個函數來處理。需要注意的是,useState 函數只會在初始渲染的時候被調用。
const [state, setState] = useState(() => { // 額外的操作someExpensiveComputation const initialState = someExpensiveComputation(props); return initialState; });
對于多個state集合的處理,還有另一種方案就是useReducer。
比如單個 state 的狀態會影響多個 state 的值的時候。
比如多個 state 的狀態會隨著某種類型的改變而改變。
如下多個 state 會隨著登錄、登出、刷新 token 這三種狀態的改變而改變。
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 提供了一種新的概念來代替生命周期函數,就是useEffect副作用。它被看作是從 React 的純函數式世界通往命令式世界的逃生通道。
它的執行時機是在屏幕元素渲染結束后延遲執行。
它的作用有:
它可以監控 state 值的變化
它可以處理只運行一次的邏輯,有點類似生命周期 componentDidMount 和 componentWillUnmount 的思維模式,
添加訂閱、設置定時器、發送網絡請求
更多其他
// 通過的第二個參數來實現只執行一次或監控state值 useEffect(() => { // ... }, []); // useEffect第一個參數的返回函數就是componentWillUnmount的思想,在組件卸載之前進行 useEffect(() => { const subscription = props.source.subscribe(); return () => { // 清除訂閱 subscription.unsubscribe(); }; });
但是這種延遲執行的機制不能滿足我們所有的場景,如果我們想要實現屏幕繪制和副作用同步執行,比如實時修改dom結構等這樣的場景,useEffect無法滿足,會出現閃屏的效果。
我們可以通過useLayoutEffect來實現,它的執行時機是在組件加載完成后,屏幕繪制之前進行。但這樣也有缺點就是阻塞屏幕渲染,可能會出現白屏或停頓。
所以useLayoutEffect的使用場景:
防止閃爍,比較耗時的計算
Dom操作
componentDidMount和componentDidUpdate的場景
如果只是單獨的獲取(get操作)就沒有必要使用useLayoutEffect。
組件傳值
組件傳值的核心:
父傳子,通過在子組件設置屬性;
子傳父,通過回調。
多級組件,通過中間狀態管理
// 父組件 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> ); }
元素節點操作
hook通過useRef來創建節點對象,然后通過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> </> ); }
我們可能會封裝一些邏輯,自定義一些屬性,暴露給父元素,那么我們就會用到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} /> // 父組件可以直接調用inputRef.current.focus()
因為 ref 對象不會把當前 ref 值的變化通知給我們,所以我們必須通過useState和useCallback實現。
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
在代碼中,我們會有一些共用的邏輯,我們可以抽離出來比如自定義的防抖節流,自定義 Hook 是一個函數,其名稱以 “use” 開頭,函數內部可以調用其他的 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} /> {b} {a} </div> ); };
性能優化
單向數據流,各組件層次分明,狀態明確,但是當項目體量大,組件嵌套多的時候,性能損耗也非常大,所以我們會做一些性能優化的工作。
可能的優化場景有:
單個 state 的更新會影響全局,有一點需要注意的是,被context包裹的組件,只要value的任何一個變動,都會重新渲染,useMemo和useCallback就會失效。
相同的輸入,不再重新計算
父組件的函數在子組件使用的時候
其實useMemo和useCallback的核心思想相同,都是記錄上一次的輸入,如果下一次輸入與上一次相同,將不會計算,直接獲取上一次的結果。他們的區別只是形式上的,useMemo返回一個 memoized 值。而useCallback返回的是memoized回調函數。
useMemo緩存計算結果的值。
useCallback主要用于緩存函數。
useCallback(fn, deps) 相當于 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,簡單講就是把函數的計算結果緩存起來,比如遞歸。
const memoize = function(fn) { const cache = {}; return function() { const key = JSON.stringify(arguments); var value = cache[key]; if(!value) { console.log('新值,執行中...'); // 為了了解過程加入的log,正式場合應該去掉 value = [fn.apply(this, arguments)]; // 放在一個數組中,方便應對undefined,null等異常情況 cache[key] = value; } else { console.log('來自緩存'); // 為了了解過程加入的log,正式場合應該去掉 } return value[0]; } } module.exports = memoize; const memoize = require('./memoize.js'); const log = console.log; // 斐波那契數組 const fibonacci = (n) => { return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); }; const memoizeFibonacci = memoize(fibonacci); log(memoizeFibonacci(45)); // 新值,執行中...; 1134903170 // 等待時間比較長 log(memoizeFibonacci(45)); // 來自緩存; 1134903170 log(memoizeFibonacci(45)); // 來自緩存; 1134903170 log(memoizeFibonacci(45)); // 來自緩存; 1134903170
到此,相信大家對“如何學習React-Hook”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。