React 的函數組件


寫在前面

React 的函數組件是 React 組件的另一種定義方式,兩種方式都可以用於定義組件,但是相比於類組件,函數組件要更簡單好用些。

組件名一般要大寫,是為了在組件使用時與一般的 html 標簽區分開

1. 創建方式

函數組件的創建方式就是定義一個函數,這個函數 return React組件。因此,返回一個 React 元素的函數就是組件。在 ES6 語法中,函數有兩種定義方式,普通函數和箭頭函數,因此,函數組件的方式也是兩種。

const Hello = () => {
      return <div>Hello Word</div>
}
function Hello(){
      return <div>Hello Word</div>
}

2. 外部數據 props

函數接收外部數據的方式就是在函數的參數里拿到,如下:

const Hello = (props) => {
      return <div>{props.msg}</div>
}

3. 內部數據 state

函數組件沒有 state,React v16.8.0 推出了 Hooks API,提供了 React.useState API解決了此問題。

函數組件的 state 數據的設置和獲取是提供了一個接口 React.useState(初始值),用於設置 state 的初始值,對 state 進行初始化。函數里面的參數為初始值,函數返回一個數組,數組的第一個是讀數據方式,第二個是寫數據方式。讀數據變量命名為 xx 時,寫數據方式一般命名為 setXX。

const Welcom = ()=>{
  const [n,setN] = React.useState(0);//析構賦值
  //等價於
  /*
  const state = React.useState(0);
  const n = state[0];
  const setN = state[1];
  */
  return (
    <div className="wel">
    n: {n}
		<button onClick={()=>setN(n+1)}>+1</button>
    </div>
  )
}

或者是先引入 useState,后面就不用再加 React. 了

import React, { useState } from 'react';
const Welcom = ()=>{
   const [n,setN] = useState(0);
}

同樣的,setN 也是一個異步函數,不會立馬執行。和 setState 不同的是,setState 是等一會后會改變 state ,而 setN 是永遠不會改變 n,而是產生新的 n。

函數組件的 state 的寫和 class 類組件的寫是完全不同的,函數組件的 setState 不會自動合並之前的值。如下:

const A = ()=>{
      const [user,setUser] = React.useState({name: 'jack', age: 18});
      const onChangeName = ()=>{
            setUser({
                  //必須加上這一句拷貝之前的數據: ...user,
                  name: 'lisa'    //這樣寫是完全錯誤的,不會自動合並之前的 state
            })
      }
      return (
            <div className="App">
                  <h1>{user.name}</h1>
                  <h1>{user.age}</h1>
                  <button onClick={onChangeName}>change name</button>
            </div>
      )
}

而且,函數組件的 setState 在接受同一個 state 對象時,即使其對象里的屬性變了,但對象地址沒變,是不會更新視圖的。

const A = ()=>{
      const [user,setUser] = React.useState({name: 'jack', age: 18});
      const onChangeName = ()=>{
            user.name = 'lisa',
            setUser(user)   //錯誤,React 發現接收的對象的地址是同一個,則會認為數據沒有改變,不會更新視圖
      }
      return (
            <div className="App">
                  <h1>{user.name}</h1>
                  <h1>{user.age}</h1>
                  <button onClick={onChangeName}>change name</button>
            </div>
      )
}

應該產生一個新的 state 對象傳入:

const onChangeName = ()=>{
            setUser({
                  ...user,
                  name: 'lisa'
            })   
      }

函數中的 setState 注意事項:

1. 不會自動合並屬性

2. 地址一定要變

4. 函數組件模擬生命周期

函數組件同樣地沒有生命周期,但是 React Hooks API 提供了 React.useEffect 來解決此問題。

React.useEffect(fn, ??) 第一個參數是在特定時機到的時候執行的回調函數,第二個參數是指明什么時機。

4.1 模擬 componentDidMount 第一次渲染

在第二個參數為 [] 時只會在第一次渲染時執行

useEffect(()=>{
      console.log("第一次渲染");
},[])

4.2 模擬 componentDidUpdate (不完全模擬)

在第二個參數的數組里加上要監聽變化的數據就行,若不加數組,不傳第二個參數,則會在 state 的任意一個屬性改變時都會觸發該函數回調

useEffect(()=>{
      console.log("n 變了");
},[n])
useEffect(()=>{
      console.log("任意屬性變了");
})

但是這樣的模擬並不完全等同,因為該函數回調會在一開始在數據由未定義 undefined 到被賦值后會執行該回調函數。而 componentDidUpdate 不會再第一次渲染時執行。

因此可以 自定義 Hook 進行計數,正確模擬 componentDidUpdate 鈎子,自定義 useUpdate.js 如下:

const useUpdate = (fn, dep)=>{
      const [count,setCount] = useState(0)
      useEffect(()=>{
            setCount(x => x + 1);
      },[dep])
      useEffect(()=>{
            if(count > 1){
                  fn()
            }
      },[count,fn])
}

4.3 模擬 componentWillUnmount

模擬銷毀前執行時期,是用函數里返回函數的方式。

useEffect(()=>{
      console.log("任意屬性變了");
      return ()=>{
            console.log("該組件要銷毀了")
      }
})

4.4 其他組件的模擬

constructor 鈎子的模擬就是在函數組件中,return 語句前的所有語句,這些語句都是在初始化函數組件的數據。

render 鈎子就是函數組件中 return 返回的內容


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM