【React】來學React18 / Router 6

React Hooks 

useState

申明響應式變量時我們會使用到的hook,

const [ ①state , ②dispatch ] = useState(③initData)

這邊使用到 array 的解構語法,也就是將 useState 回傳的 array 中的值個別取出來,counter 為 state 變數,setCounter 則是負責改變 state 變數的函式,一般命名傳統習慣將此函式命名為 set+state 變數名稱,需要特別注意的是更改state一律只能透過set(setCounter)函式更改,useState所帶的參數為這個 state 的初始值,而一個component中可以有多個state,如果兩次 dispatchAction 傳入相同的 state 值,那麼組件就不會更新。

注意事項:

  • 僅頂層調用 Hook :不能在循環,條件,嵌套函數等中調用useState()。在多個useState()調用中,渲染之間的調用順序必須相同。
  • React 18 後的版本, setState 都是以非同步的方式更新
setState本身一直一個同步函數, 我們指的是調用完setState後react會同步的去執行後續的步驟還是會異步的去執行後續的步驟

來看一個例子:

下面的 code,即使點擊一次 button,counter 也只會 + 1

export default function App() {
  const [counter, setCounter] = useState(0);

  const handleClick = () => {
      setCounter(counter + 1);
      setCounter(counter + 1);
  }

  return (
    <div className='App'>
      <h1>Function Component</h1>
      <div>
        counter: {counter}
      </div>
      <br/>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}

當這段 code 被送出去時,state 的指向的是過去的 state,換句話說,這段可以看成這樣: 

setCounter(0 + 1);
setCounter(0 + 1);

如果上面的 Code 要改成 +2,則可以:

const increase = prevCounter => prevCounter + 1
const handleClick = () => {
      setCounter(increase);
      setCounter(increase);
  }

// (推薦) 在setCount裡的函數中傳入一個方法
const handleButton = () => {
  setCounter(prev => prev + 1)
  setCounter(prev => prev + 1)
  setCounter(prev => prev + 1)
  setCounter(prev => prev + 1)
}

接下來在看下另外個可能遇到的坑,實際會發現count並沒有像想像中一樣輸出1,而是0

const [count, setCount] = useState(0)  
const onCountup = () => {
  setCount( count + 1 );
  console.log(count)
}

以下為幾個可以用到的解決方法 

 1. 使用useEffect

const [count, setCount] = useState(0)
useEffect(() => {
 console.log(count)
}, [count])

<button onClick={() => {
 setCount(count + 1)
}}>加一</button>

2. 將變量存起來 

const [square, setSquare] = useState(0)
const [count, setCount] = useState(0)

const onCountup = () => {
  const newCount = count + 1
  setCount( newCount );
  setSquare( newCount * newCount );
}

3. 推薦 在setCount裡的函數中傳入一個方法

const onCountup = () => {
  setCount(preCount => {
    const newCount = preCount + 1;
    setSquare( newCount * newCount );
    return newCount;
  });
}

 在React 18更新中,將所有狀態更新都會執行automatic batching。因此以下例子在React 18中, count依然會是1,而且只會re-render一次。

export function Counter(){
    const [count, setCount] = useState(0)
    const handleClick = ()=>{
        async function callServer(){

            const res = await fetch('/count')
            const result = await res.json()
            setCount(count + 1)
            setCount(count + 1)
            // Automatic batching 
        }
    }
    return (
        <div>
            <button onClick={handleClick}>
        </div>
    )
}

useEffect✅

在function組件中,當想在組件完成掛載,dom渲染完成,做一些操縱dom,請求數據,那麼useEffect是一個不二選擇。useEffect是一個接受兩個參數的函數。傳遞給useEffect的第一個參數是一個名為effect的函數(你可以猜到為什麼這個鉤子叫useEffect),第二個參數(是可選的)是一個存儲依賴關係的數組。
特別注意的是,如果不給useEffect執行加入限定條件,函數組件每一次更新都會觸發effect ,那麼也就說明每一次state更新,或是props的更新都會觸發useEffect執行,此時的effect又充當了componentDidUpdate和componentwillreceiveprops,所以說合理的用於useEffect就要給effect加入限定執行的條件,也就是useEffect的第二個參數,這裡說是限定條件,也可以說是上一次useeffect更新收集的某些記錄數據變化的記憶,在新的一輪更新,useeffect會拿出之前的記憶值和當前值做對比,如果發生了變化就執行新的一輪useEffect的副作用函數,useEffect第二個參數是一個數組,用來收集多個限制條件 。

useEffect 第一個參數 callback, 返回的 destory , destory 作爲下一次 callback 執行之前調用,用於清除上一次 callback 產生的副作用。

第二個參數作爲依賴項,是一個數組,可以有多個依賴項,依賴項改變,執行上一次 callback 返回的 destory ,和執行新的 effect 第一個參數 callback 。

對於 useEffect 執行, React 處理邏輯是採用異步調用 ,對於每一個 effect 的 callback, React 會向 setTimeout 回調函數一樣,放入任務隊列,等到主線程任務完成,DOM 更新,js 執行完成,視圖繪製完畢,才執行。所以 effect 回調函數不會阻塞瀏覽器繪製視圖。

useEffect(()=>{
    return destory
},dep)

如下在 useEffect 中做的功能如下:

  • ① 請求數據。

  • ② 設置定時器, 延時器等。

  • ③ 操作 dom , 在 React Native 中可以通過 ref 獲取元素位置信息等內容。

  • ④ 註冊事件監聽器, 事件綁定,在 React Native 中可以註冊 NativeEventEmitter 。

  • ⑤ 還可以清除定時器,延時器,解綁事件監聽器等。

記得要清除副作用 

function getUserInfo(a){
    return new Promise((resolve)=>{
        setTimeout(()=>{ 
           resolve({
               name:a,
               age:16,
           }) 
        },500)
    })
}

const Demo = ({ a }) => {
    const [ userMessage , setUserMessage ] :any= useState({})
    const div= useRef()
    const [number, setNumber] = useState(0)
    /* 模擬事件監聽處理函數 */
    const handleResize =()=>{}
    /* useEffect使用 ,這裏如果不加限制 ,會是函數重複執行,陷入死循環*/
    useEffect(()=>{
       /* 請求數據 */
       getUserInfo(a).then(res=>{
           setUserMessage(res)
       })
       /* 定時器 延時器等 */
       const timer = setInterval(()=>console.log(666),1000)
       /* 操作dom  */
       console.log(div.current) /* div */
       /* 事件監聽等 */
       window.addEventListener('resize', handleResize)
         /* 此函數用於清除副作用 */
       return function(){
           clearInterval(timer) 
           window.removeEventListener('resize', handleResize)
       }
    /* 只有當props->a和state->number改變的時候 ,useEffect副作用函數重新執行 ,如果此時數組爲空[],證明函數只有在初始化的時候執行一次相當於componentDidMount */
    },[ a ,number ])
    return (<div ref={div} >
        <span>{ userMessage.name }</span>
        <span>{ userMessage.age }</span>
        <div onClick={ ()=> setNumber(1) } >{ number }</div>
    </div>)
}

依舊來整理下可能遇到的坑

 function App(props){
    const [data, setData] = useState(null);
    const fetchData = () => {
         //fetch some data 
    }
    useEffect(() => {
        fetchData(); //Invoked inside useEffect
    }, [fetchData])

}
在外面定義一個函數,然後在effect裡面調用它。上面的情況會導致每次渲染都會調用fetchData,因為傳遞的依賴是一個函數,而函數是對象。 React會比較上一次和當前渲染的fetchData,兩者是不一樣的,因此會觸發對effect的調用。

我們可以使用下面方法代替

useEffect(() => {
  // declare the data fetching function
  const fetchData = async () => {
    const data = await fetch('https://yourapi.com');
  }

  // call the function
  fetchData()
    // make sure to catch any error
    .catch(console.error);
}, [])

useEffect 清理機制

import React, { useState, useEffect } from "react";

export default function Post() {
  const [posts, setPosts] = useState([]);
  const [error, setError] = useState(null);
  useEffect(() => {
    const controller = new AbortController();
    const signal = controller.signal;

      fetch("https://jsonplaceholder.typicode.com/posts", { signal: signal })
      .then((res) => res.json())
      .then((res) => setPosts(res))
      .catch((err) => {
        if (err.name === "AbortError") {
          console.log("successfully aborted");
        } else {
          setError(err);
        }
      });
    return () => controller.abort();
  }, []);
  return (
    <div>
      {!error ? (
        posts.map((post) => (
          <ul key={post.id}>
            <li>{post.title}</li>
          </ul>
        ))
      ) : (
        <p>{error}</p>
      )}
    </div>
  );
}

React 常見CSS管理方式

  • 一:namespaces
  • 二:CSS in JS
  • 三:CSS Modules
  • 四:utility-first CSS Framework

那麼如果我們想再要useEffect中調用個異步的方法時 

useCallback✅

useContext✅

useLayoutEffect ✅

useMemo ✅

避免重複進行複雜耗時的計算

useState派發更新函數的執行,就會讓整個function組件從頭到尾執行一次,所以需要配合useMemo,usecallback等api配合使用

useReducer✅

useRef✅

flushSync

以上不定時更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值