10 mins 帶你入門 React Hooks

Why Hooks

  1. Function Component 可使用狀態
  2. 簡化 Class Component 的生命週期
  3. 無需爲 this 指向問題傷破腦經
  4. 解決 render props & HOC 嵌套缺陷
  5. 官方爸爸讓你學

How to use Hooks

如今開始咱們用建立一個計數器的方式開始學習使用React Hooksjavascript

useState

useState 的用法很簡單,傳入一個初始 state,返回一個 state 以及修改 state 的函數。html

// useState 返回的 state 是個常量
// 每次組件從新渲染以後,當前 state 和以前的 state 都不相同
// 即便這個 state 是個對象
const [count, setCount] = useState(1)
複製代碼

setCount 就至關於 setState ,能夠傳入一個新的狀態或者函數。例如:java

setCount(2)
setCount(prevCount => prevCount + 1)
複製代碼

其實 useState 內部能夠簡單理解爲(只是理解):react

// 閉包存儲 state 數據
function useState(initialState){
    let state = initialState;
    function setState = (newState, action) => {
        state = newState;
    }
    return [state, setState]
}
複製代碼

那接下來編寫 Count 組件:redux

function Counter() {
  const [count, setCount] = React.useState(0)
  return (
    <div> Count: {count} <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> </div>
  );
}
複製代碼

useEffect

如今咱們有了新的需求: 在 count 每次更新的時候打印 count 的值,這時咱們須要用 useEffect 來實現閉包

function Counter() {
  const [count, setCount] = React.useState(0)
  
  React.useEffect(() => {
    console.log(count)
  })
  
  return (
    <div> Count: {count} <button onClick={() => setCount(count + 1)}>+</button> <button onClick={() => setCount(count - 1)}>-</button> </div>
  );
}
複製代碼

以上在每次從新渲染時, useEffect 就能夠實現打印,能夠把它看成是 componentDidUpdatecomponentDidMount, 同於 Class Component中的函數

componentDidMount() {
  console.log(count)
}
componentDidUpdate() {
  console.log(count)
}
複製代碼

另外它能夠返回一個函數,用於解綁一些反作用,功能相似於 componentWillUnmount學習

React.useEffect(() => {
    console.log(count)
    // 注意: 當咱們每次更新計數時,都會先打印 clean 這行 log
    return () => console.log('clean', count)
})
複製代碼

爲了防止咱們沒必要要的渲染,useEffect 加入第二個參數, 只有在 count 改變的時候纔會執行。優化

React.useEffect(() => {
    console.log(count)
    // 當咱們每次更新計數時,都會先打印 clean 這行 log
    return () => console.log('clean', count)
}, [count])
複製代碼

useReducer

useReduceruseState 的替代方案,主要是爲了解決多個 state 子值的問題。(會Redux的一看就會明白)ui

看一下🌰:

// 初始化 state
const initialState = {count: 0};

// 類於 redux 的 reducer
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div> Count: {state.count} /* dispatch 調用 action 操做 */ <button onClick={() => dispatch({type: 'decrement'})}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</button> </div>
  );
}
複製代碼

useMemo

這是個用於優化的 hook, 返回一個 memoized 值。說白了就是會利用閉包的特性存儲上次的參數,若是和此次傳入的參數一致就返回以前的結果。這叫作記憶化技術。

下面咱們來看個反🌰:

function WithoutMemo() {
    const [count, setCount] = useState(1);
    const [val, setValue] = useState('');

    function expensive() {
        console.log('compute');
        let sum = 0;
        for (let i = 0; i < count * 100; i++) {
            sum += i;
        }
        return sum;
    }

    return <div> <h4>{count}-{val}-{expensive()}</h4> <div> <button onClick={() => setCount(count + 1)}>+c1</button> <input value={val} onChange={event => setValue(event.target.value)}/> </div> </div>; } 複製代碼

這個函數式組件面臨的問題就是 expensive 方法無論是 count 仍是 val 改變時都會因爲組件的從新渲染, 從新計算。

可是這裏的昂貴計算只依賴於count的值,在val修改的時候,是沒有必要再次計算的。在這種狀況下,咱們就可使用useMemo,只在count的值修改時,執行expensive計算:

// count 若是不變就不會再次執行
const expensive = React.useMemo(() => {
    console.log('compute');
    let sum = 0;
    for (let i = 0; i < count * 100; i++) {
        sum += i;
    }
    return sum;
}, [count]);
複製代碼

useCallback

useCallbackuseMemo 很類似,不過它返回的是函數。下面繼續看個🌰:

function Parent() {
    const [count, setCount] = React.useState(1);
    const [val, setVal] = React.useState('');

    const callback = React.useCallback(() => {
        return count;
    }, [count]);
    
    return <div>
        <h4>{count}</h4>
        // callback 做爲 props 傳入
        <Child callback={callback}/>
        <div>
            <button onClick={() => setCount(count + 1)}>+</button>
            <input value={val} onChange={event => setVal(event.target.value)}/>
        </div>
    </div>;
}

function Child({ callback }) {
    const [count, setCount] = React.useState(() => callback());
    
    React.useEffect(() => {
        setCount(callback());
    }, [callback]);
    
    return (
        <div>
            {count}
        </div>
    )
}
複製代碼

如今就是有一個父組件,其中包含子組件,子組件接收一個函數做爲props;一般而言,若是父組件更新了,子組件也會執行更新;可是大多數場景下,更新是沒有必要的,咱們能夠藉助useCallback來返回函數,而後把這個函數做爲props傳遞給子組件;這樣,子組件就能避免沒必要要的更新。

useCallback(fn, deps) 至關於 useMemo(() => fn, deps)。

自定義 Hook

經過自定義 Hook,能夠將組件邏輯提取到可重用的函數中。

自定義 Hook 是一個函數,其名稱以 use 開頭,函數內部能夠調用其餘的 Hook

function useConsoleCount(count) {
  React.useEffect(() => {
    console.log(count)
    // 當咱們每次更新計數時,都會先打印 clean 這行 log
    return () => console.log('clean', count)
  }, [count])
}

function Counter() {
  const [count, setCount] = React.useState(0);
  
  useConsoleCount(count)
  
  return (
    <div> Count: {count} <button onClick={() => setCount(count + 1)}>+</button> <button onClick={() => setCount(count - 1)}>-</button> </div>
  );
}
複製代碼

結尾

  • 這篇只是簡單介紹hook的使用,有錯誤的地方歡迎指正
  • 關於其餘的 hook 可直接閱讀官方文檔
相關文章
相關標籤/搜索