对React Hooks的理解


一、Why

React 创建组件的方式,最常见的有两种,一个是类组件,例如 class Dashboard extends React.Component, 一种是纯函数组件, 例如 const Dashboard = () => {return <></>}, 而我们希望组件不要变成复杂的容器,最好只是数据的管道。所以组件的最佳写法应该是函数,而不是类。
但是,我们知道穿函数组件与类组件的区别是很大的,如:

纯函数组件

  • 纯函数组件没有状态,只有接收的props
  • 纯函数组件没有生命周期
  • 纯函数组件没有this
  • 只能是纯函数

类组件

  • 遇到简单的页面,代码会显得很重。
  • 每创建一个类组件,都要去继承一个React 实例。
import React from 'react'

class AddCount extends React.PureComponent {
  constructor(props){
    super(props)
    this.state={
      count: 0
    }
  }
  addcount = () => {
    let newCount = this.state.count
    this.setState({
      count: newCount +=1
  })
  }
  render(){
    return (
      <div>
        <p>{this.state.count}</p>
        <button onClick={this.addcount}>count++</button>
      </div>
    )
  }
}
export default AddCount

纯函数的这些特点,就注定纯函数组件只能做UI展示的功能,涉及到state,reducer我们就必须用类组件的形式,但是类组件又有上面明显的缺陷,所以为了解决以上的问题,React 设计出了React Hooks。所以React Hooks就是纯函数组件的升级版,可以完全不用class, 就能写出一个全功能的组件。

二、What

React Hooks的意思就是,组件尽量写成纯函数,如果需要跟函数外部环境发生的交互,就用Hooks 把外部代码“钩”进来,而React Hooks, 就是我们所说的“钩子”

三、How

React 为我们提供了一些常用的Hooks, 当然我们也可以根据自己的需求,自定义我们自己的Hook。我们可以看到react hooks 都是以use为前缀,所以react约定,钩子一律使用use作为前缀。

基础 Hook

  • useState
  • useEffect
  • useContext

额外的 Hook

  • useReducer
  • useCallback
  • useMemo
  • useRef
  • useImperativeHandle
  • useLayoutEffect
  • useDebugValue

四、Use

1. useState

Link

const [state, setState] = useState(initialState);

返回一个 state,以及更新 state 的函数。
在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。
setState 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列。

function TestPage() {
    const [count, setCount] = useState(0);
    return (
        <>
        Count: {count}
        <button onClick={() => setCount(0)}>Reset</button>
        <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
        <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
        </>
    )
}

2. useEffect

Link

useEffect(didUpdate);

该 Hook 接收一个包含命令式、且可能有副作用代码的函数。

在函数组件主体内(这里指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的 bug 并破坏 UI 的一致性。

使用 useEffect 完成副作用操作。赋值给 useEffect 的函数会在组件渲染到屏幕之后执行。你可以把 effect 看作从 React 的纯函数式世界通往命令式世界的逃生通道。

默认情况下,effect 将在每轮渲染结束后执行,但你可以选择让它 在只有某些值改变的时候 才执行。

const AsyncPage = ({ name }) => {
    const [loading, setLoading] = useState(true)
    const [person, setPerson] = useState({})
    useEffect(() => {
        setLoading(true)
        setTimeout(() => {
            setLoading(false)
            setPerson({ name })
        }, 2000)
    }, [name])
    return (
        <>
            {loading ? <p>Loading...</p> : <p>{person.name}</p>}
        </>
    )
}

3. useContext

Link

const value = useContext(MyContext);

接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。

const themes = {
    light: {
      foreground: "#000000",
      background: 'red'
    },
    dark: {
      foreground: "#ffffff",
      background: 'green'
    }
  };
  
  const ThemeContext = React.createContext(themes.light);
  
  function TestPage() {
    return (
      <ThemeContext.Provider value={themes.dark}>
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
  
  function Toolbar(props) {
    return (
      <div>
        <ThemedButton />
      </div>
    );
  }
  
  function ThemedButton() {
    const theme = useContext(ThemeContext);
    return (
      <button style={{ background: theme.background, color: theme.foreground }}>
        I am styled by theme context!
      </button>
    );
  }

最终实现效果
在这里插入图片描述

4. Create ourself hooks

Link
通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。

const usePerson = (name) => {
    const [loading, setLoading] = useState(true)
    const [person, setPerson] = useState({})

    useEffect(() => {
        setLoading(true)
        setTimeout(() => {
            setLoading(false)
            setPerson({ name })
        }, 2000)
    }, [name])
    return [loading, person]
}

const AsyncPage = ({ name }) => {
    const [loading, person] = usePerson(name)
    return (
        <>
            {loading ? <p>Loading...</p> : <p>{person.name}</p>}
        </>
    )
}

const PersonPage = () => {
    const [state, setState] = useState('')
    const changeName = (name) => {
        setState(name)
    }
    return (
        <>
            <AsyncPage name={state} />
            <button onClick={() => { changeName('名字1') }}>名字1</button>
            <button onClick={() => { changeName('名字2') }}>名字2</button>
        </>
    )
}

五、Summarize

React 官网是这样说的

  • 完全可选的, 你无需重写任何已有的代码就可以在一些组件中尝试Hook,但是如果你不想,你不必现在就去学习或是用Hook
  • 100% 向后兼容,Hook不包含任何破坏性改动
  • 现在可用,Hook已发布于v16.8.0, RN从0.59版本开始支持
  • 没有计划从React中移除class
  • Hook不会影响你对React的理解,恰恰相反,Hook为已知的React概念提供了更只记得API,如:props,state, context,ref以及生命周期

参考: https://www.jianshu.com/p/d600f749bb19


版权声明:本文为qq_33495944原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。