react hook 的 useEffect 副作用

前言

在 react 的 16.8 版本中 引入了 hook 概念,這是一次翻天覆地的改變,是一次理念的改變,也可以說是推到重建

 


 

本文的目的

在開始寫本文之前,一直在考慮要不要對比舊版本的react,也就是 class component(hook 被稱爲 function component)。因爲對比着更容易找到異同點,更容易上手去使用。
但是,react hook 是一次完全的更新換代,理念也改變,對比 class 進行介紹,可以說是一種誤導。
所以,這段話寫在前面,希望各位讀者可以對比着快速上手,但是在理解hook時,請不要用舊版本的那一套去理解

 


 

舊版本的react(v16.8以前)

舊版的react也就是 class component,當然新版本中仍然還兼容着class 用法

 class ListPage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
    console.log('constructor');
  }

  componentWillMount() {
    console.log('componentWillMount');
  }

  componentDidMount() {
    console.log('componentWillMount');
  }

  componentDidUpdate() {
    const { count } = this.state;
    console.log('componentDidUpdate, count', count);
  }

  componentWillUnmount() {
    console.log('componentWillUnmount');
  }

  render() {
    console.log('render');
    const { count } = this.state;
    return(
      <div>
        <span> { count } </span>
        <button onClick={() => this.setState({count: count + 1})}>增加count</button>
      </div>
    )
  }
}

初次渲染時,打印的log:

constructor
componentWillMount
render
componentDidMount

state改變時,打印的log:

componentWillUpdate
render
componentDidUpdate

 


 

新版本的 function Component 的 用法(即 hook)

useEffect,副作用,可以看做是 componentDidMount componentDidUpdate componentWillUnmount 三個函數的組合,但最好不要使用生命週期那一套來理解

const Home = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('useEffect 1');

    return () => {console.log('useEffect 2');}
  }, [])

  useEffect(() => {
    console.log('useEffect 3');

    return () => {console.log('useEffect 4');} 
  }, [count])
  
  console.log('render')

  return(
    <div>
      <span>{count}</span>
      <button onClick={() => setCount(count + 1)}>增加count</button>
    </div>
  )
}

初次渲染時,打印的log:

render
useEffect 1
useEffect 3

state改變時,打印的log:

render
useEffect 4
useEffect 3

通過上面的log我們可以發現:

  1. useEffect 可以模擬出上文所說的 三個聲明週期(componentDidMount componentDidUpdate componentWillUnmount)
    componentWillUnmount 暫未使用log嘗試(log('useEffect 2')所在的回調中能夠完整代替 componentWillUnmount)
  2. class component 和 function component 比較可以發現:並沒有完全的契合
  3. log('useEffect 1') 雖然可以模擬出 componentDidMount,但其他的log('useEffect 3')也會觸發
  4. 可以發現 function component 中已經沒有了 will的生命週期(比如willMount、willUpdate)

 


 

useEffect

 useEffect(() => {
    console.log('useEffect 1');

    return () => {console.log('useEffect 2');}
  }, [])

  useEffect(() => {
    console.log('useEffect 3');

    return () => {console.log('useEffect 4');} 
  }, [count])

useEffect 鉤子有兩個參數,參數一是function,參數二是array數組,通過這兩個參數可以實現function component不同的情景

  1. 如果參數二不傳參
    包括初始執行、state改變,參數一的function回調都會執行
  2. 如果參數二傳參空數組
    只在初始時執行參數一的function回調
  3. 如果參數二傳有值得數組
    初始時,或者參數二數組中值改變時,都會執行參數一的function回調

注意: 參數一的function回調,可以有返回值(也是function回調)
如果參數二傳入的是空數組,此處返回值的回調即可代表 componentWillUnmount,即在function銷燬時執行

 


 

最後:

  1. useEffect 與其看做是三個生命週期函數的集合,不如看做每次渲染後都會執行的副作用(初始化、state改變時都會重新渲染)
  2. 每次render 都有自己的 props 和 state,就像快照,每一個render瞬間都有自己獨立的function
    所以不要想着在 每一個快照中都能拿到 最新的state,如下的count:
 useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1);
      // 此處打印始終是1,因爲count綁定了初次進入的值
      console.log('count', count)
      // 調整爲此即可,或者將count放入參數二的數組中(但這樣會導致計算器的頻繁創建和銷燬)
      // setCount(preCount => preCount + 1)
    }, 1000);
    return () => clearInterval(id);
 }, []);

 


 

參考

精讀《useEffect 完全指南》: https://www.cnblogs.com/ascoders/p/10591832.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章