React(11) - class & function component 比較 & useRef


Posted by TempuraEngineer on 2022-11-14

目錄


差異

class component有生命週期,那還要function component做甚麼?🤔

先說結論,class component和function component絕對不只差在預設有無生命週期

它們的關鍵差在於function component會抓住render時(一個render就是一幀)props傳進來的值,而class component則否

Function components capture the rendered values.

每次組件的資料更新function(component)會被呼叫,之後會render,每次render都會產生一幀

function每次被呼叫都會產生function scope,也就是說前一幀和後一幀的scope是互相獨立的,且組件內的變數都是常數(constant)

以下面這段code來說就是,組件資料更新時,都會產生新的scope,裡面都有一個常數num(舊的num和新的num互相獨立)

const [num, setNum] = useState(0);

以這個例子(取自How Are Function Components Different from Classes?)來說

function component在按了鈕後迅速切換選項的情況下,最後setTimeout會得到舊的值

因為setTimeout被排入event loop的當下,它抓住了那一幀的props的值(舊值),然後在3秒後才印出來

  // 上略

  const handleClick = () => {
    setTimeout(() => alert('Followed ' + props.user), 3000);
  };

  // 下略

同樣的操作發生在class component,最後setTimeout會得到新的值

這是因為React的props是immutable,但this是mutable

只要實例不被消滅,隨著資料更新this也會被更新,所以才會得到新的值

  // 上略

  handleClick = () => {
    setTimeout(alert('Followed ' + this.props.user), 3000);
  };

  // 下略

所以說按了鈕後故意馬上unmount組件(消滅實例),就會得到和function component一樣的結果

class Bar extends Component {
  constructor(props) {
    super(props);
    this.state = {

    };
  }

  componentDidUpdate(){
    setTimeout(() => {
      console.log(`Bar: ${this.props.name}`);

    }, 3000);
  }

  render() { 
    return (
      <div>
        name of Bar is {this.props.name}
      </div>
    );
  }
}
function App(){
  const [nickName, setNickName] = useState('Emma');
  const [arr, setArr] = useState([1]);

  return (
    <div>
      {
        // Bar依賴父組件的資料arr,arr內有幾個元素,就有幾個Bar被mount
        arr.map((i, index) => <Bar key={index} name={nickName}></Bar>)
      }

      <select className='block my-3' onChange={(e) => {setNickName(e.target.value)}}>
        <option value="Alex" key="Alex">Alex</option>
        <option value="Joyce" key="Joyce">Joyce</option>
      </select>

      {/* 藉由在父組件清空陣列來unmount子組件Bar */}
      <SimpleButton click={() => {setArr([])}}>clear arr</SimpleButton>
    </div>
  )
}

但如果不想unmount組件呢🤔

使用解構將name抽出,切斷props和this的連結

這樣componentDidUpdate每次執行都會創造出不同的function scope

// Bar

  // 上略

  render() { 
    const props = this.props;

    setTimeout(() => {
      console.log(`Bar: ${props.name}`);

    }, 3000);

    return (
      <div>
        name of Bar is {this.props.name}
      </div>
    );
  }

  // 下略


useRef

useRef()是一個hook

它的current屬性就像就像實例的屬性,可以存任何值,而且是mutable

The useRef() Hook isn’t just for DOM refs. The “ref” object is a generic container whose current property is mutable and can hold any value, similar to an instance property on a class.

透過它可以得到像上面的例子中class component一樣的結果(按了鈕後迅速切換選項,最後setTimeout會得到新的值)

function Foo(props){
  const name = useRef('Emma');

  useEffect(() => {
    name.current = props.name;

    setTimeout(() => {
      console.log(name.current);
      console.log(props.name);

    }, 3000);

  });

  return (
    <p>name of Foo is {props.name}</p>
  )
}

function App(){
  const [nickName, setNickName] = useState('Emma');

  return (
    <div>
      <Foo name={nickName}></Foo>

      <select className='block my-3' onChange={(e) => {setNickName(e.target.value)}}>
        <option value="Alex" key="Alex">Alex</option>
        <option value="Joyce" key="Joyce">Joyce</option>
      </select>
    </div>
  )
}


參考資料

從實際案例看 class 與 function component 的差異
How Are Function Components Different from Classes?
How to fetch data with React Hooks
A Complete Guide to useEffect


#React #useRef #Class component #Function component







Related Posts

關於 React 小書:可把函式當作參數傳入 setState

關於 React 小書:可把函式當作參數傳入 setState

Laravel 入門:在 Mac 安裝與啟動 Laravel

Laravel 入門:在 Mac 安裝與啟動 Laravel

JS30 Day 12 筆記

JS30 Day 12 筆記


Comments