React Hooks上篇-简介

1,031 阅读2分钟

Hooks 要解决的问题

组件之间的逻辑复用

现有的常用方案是 render propshigher-order components ,同时也带了问题: 重新调整组件结构

逻辑调用链长,难以理解

形成组件的嵌套地狱

复杂组件难以理解

组件越来越复杂:

初期简单的类组件随着功能的增多变得越来越复杂,也越来越难以拆分

生命周期机制把相关的代码分离在不同的部分,把不相关的代码又合在一起,容易引入bug

引入各种各样的库,提升项目复杂度

类组件的冗杂

类组件有许多不方便之处: this

绑定事件处理程序

代码冗长

......

Hooks 核心思想

减少组件嵌套,聚合相关逻辑,抽取细粒度的可复用组件

Hooks 核心 API

useState(initialState)

import React, { useState } from 'react'

function Count() {
  const [count, setCount] = useState(0)
  const [lazyState, setLazyState] = useState(() => expensiveComputation())

  return (
    <div>
      <p>lazy state {lazyState}</p>
      <p>you clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>+</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
    </div>
  )
}

useEffect(fn, [dependencyList])

import React, { useState, useEffect } from 'react'

function List() {
  const [dataList, setDataList] = useState([])
  const [searchValue, setSearchValue] = useState('123')
  
  useEffect(() => {
    consoloe.log(searchValue)
  })
  
  useEffect(() => {
    async function fetchData(value) {
      const result = await fetch('xxx')
      setDataList(result.data);
    }
    fetchData(searchValue)
  }, [searchValue])

  useEffect(() => {
    function scroll() {
        /* ... */
    }
    window.addEventListener('scroll', scroll)
    return () => {
      window.removeEventListener('scroll', scroll)
    }
  }, [])

  return (
    <div>
      <input
        value={searchValue}
        onChange={e => setSearchValue(e.target.value)}
      />
      <ul>
        <li>
          {dataList.map((data, index) => <li key={index}>{data.text}</li>)}
        </li>
      </ul>
    </div>
  )
}

useLayoutEffect(fn, [dependencyList])

同步版的 useEffect,常用于 dom 更新时的元素位置、样式等属性的计算

Hooks 附加 API

useReducer(reducer, initialState)

import React, { useState, useReducer } from 'react'

/* base implements */
function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState)
  function dispatch(action) {
    const newState = reducer(state, action)
    setState(newState)
  }
  return [state, dispatch]
}

function reducer(state = 0, action) {
  switch(action.type) {
    case 'increase': return state - 1
    case 'decrease': return state + 1
    default: return state
  }
}


function Count() {
  const [count, dispatch] = useReducer(reducer, 0)
  return (
    <div>
      <p>you clicked {count} times</p>
      <button onClick={() => dispatch({action: 'increase'})}>+</button>
      <button onClick={() => dispatch({action: 'decrease})}>-</button>
    </div>
  )
}

useCallback(fn, [dependencyList])

import React, { useState, useCallback, memo } from 'react'

const Button = memo(props => {
  useEffect(() => {
    console.log('rendered')
  })

  return (
    <button onClick={props.onClick}>{props.children}</button>
  )
})

function Counter({initialCount = 0}) {
  const [count, setCount] = useState(initialCount);
  
  const memoClick = useCallback(() => {
    setCount(prevCount => prevCount + 1)
  }, [])

  return (
    <>
      Count: {count}
      <Button onClick={memoClick}>+</Button>
      <Button onClick={() => setCount(count - 1)}>-</Button>
    </>
  );
}

useMemo(() => fn, [dependencyList])

import React, { useState, useCallback, useMemo } from 'react'

function Counter({initialCount = 0}) {
  const [count, setCount] = useState(initialCount);
  
  // useCallback(fn, deps) is equivalent to useMemo(() => fn, deps)
  const memoClick = useMemo(() => () => {
    setCount(prevCount => prevCount + 1)
  }, [])
  /*...*/
}

useRef(initialValue)

import React, { useRef } from 'react'

function TextInputWithFocusButton() {
  const inputEl = useRef(null)
  const onButtonClick = () => {
    inputEl.current.focus()
  }
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClcik={onButtonClick}>Focus the input</button>
    </>
  )
}

function Button() {
  const count = useRef(null)
  return (
    <button>rendered {count.current++} times</button>
  )  
}

Hooks 推荐规则

Only Call Hooks at the Top Level

Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function.

Only Call Hooks from React Functions

Call Hooks from React function components. Call Hooks from custom Hooks

延伸阅读

Hooks API Reference

Hooks FAQ

useEffect 完整指南

如何错误使用useCallback