React Hooks的基本使用以及和类组件的区别

Hooks简介

在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行渲染。React在v16.8 的版本中推出了 React Hooks 新特性,Hook是一套工具函数的集合,它增强了函数组件的功能,hook不等于函数组件,所有的hook函数都是以use开头。

  • 使用 React Hooks 相比于从前的类组件有以下几点好处:
    • 代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的生命周期函数中,容易使开发者不利于维护和迭代,通过 React Hooks 可以将功能代码聚合,方便阅读维护
    • 组件树层级变浅,在原本的代码中,我们经常使用 HOC/render/Props 等方式来复用组件的状态,增强功能等,无疑增加了组件树层数及渲染,而在 React Hooks 中,这些功能都可以通过强大的自定义的 Hooks 来实现
  • hooks的限制
    • hook只能用在函数组件中,class组件不行
    • 普通函数不能使用hook
    • 函数组件内部的函数也不行
    • hook定义时要注意先后顺序
    • hook函数一定要放在函数组件的第一层,别放在if/for中

React Hooks的基本使用

useState 保存组件状态

import React, { useState } from 'react'

// useState 参数值,为数组1的默认值
// 对于一些有一些需要初始计算的可以使用回调函数的方式来初始值
// let [count, setCount] = useState(() => 0)
// 固定值初始值
let [count, setCount] = useState(0)

// jsx
<button onClick={
    
    () => setCount(count + 1)}>+++</button>

useEffect

  • 此hook可以模拟函数组件的生命周期,函数组件对于在一些生命周期中操作还是无能为力,所以 React提供了 useEffect 来帮助开发者处理函数组件,来帮助模拟完成一部份的开发中非常常用的生命周期方法。常被别的称为:副作用处理函数。此函数的操作是异步的。
  • useEffect 相当类组件中的3个生命周期 componentDidMount componentDidUpdate componetWillUnMount

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

const App = () => {
    
    

  let [count, setCount] = useState(0)
  // 参数1:回调函数
  // 如果没有第2个参数,表示 componentDidMount componentDidUpdate
  // 如果第2个参数为空数组 表示 componentDidMount
  // 如果第2个参数不空数组,只关注所写变量事件更新和挂载 componentDidMount componentDidUpdate

  useEffect(() => {
    
    
    if (count > 10) {
    
    
      document.title = count
    }
// 返回回调函数中就是 componetWillUnMount
//  在执行下一个 effect 之前,上一个 effect 就已被清除
    return () => {
    
    
      // 在此处,就可以把setTimout setInterval  清空
    }
  }, [count]);

  return (
    <div>
      <h1>{
    
    count}</h1>
      <button onClick={
    
    () => setCount(count + 1)}>+++</button>
    </div>
  );
}

useContext

  • 使用useContext可以方便我们获取Context中的数据源
// 父组件
import React, {
    
     useState } from 'react'
import cxt from './context/user'
import Child from './views/Child';
export default function Context() {
    
    
  const [user] = useState({
    
    })
  const {
    
    Provider} = cxt
  return (
    <div>
      <Provider value={
    
    user}>
        <Child/>
      </Provider>
    </div>
  )
}

// Context
import {
    
    createContext} from 'react'

export default createContext()
// child组件
import React,{
    
     useContext } from 'react'
import cxt from './context/user'
export default function Child() {
    
    
  const value = useContext(cxt)
  return (
    <div>
      Child {
    
    value.name}
    </div>
  )
}

useReducer

  • useReducer 这个 Hooks 在使用上几乎跟 Redux一模一样,唯一缺少的就是无法使用 redux 提供的中间件。
    import React, { useState, useReducer } from ‘react’
const initialState = {
    
     count: 0 };

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();
  }
}

const App = () => {
    
    
  const [state, dispatch] = useReducer(reducer, initialState)

  return (
    <div>
      <h3>{
    
    state.count}</h3>
      <button onClick={
    
    () => dispatch({
    
     type: 'increment' })}>+++</button>
      <button>---</button>
    </div>
  );
}

useMemo

  • 记忆组件,可以理解为计算属性
import React, {
    
     useState, useMemo } from 'react';

const Memo = () => {
    
    
  const [count, setCount] = useState(1)
  const [val, setValue] = useState('')

  // 返回上一次缓存的值
  const sum = useMemo(() => {
    
    
    return count + 10
  }, [count]);

  return (
    <div>
      <h3>{
    
    count}-{
    
    val}-{
    
    sum}</h3>
      <div>
        <button onClick={
    
    () => setCount(count + 1)}>+ count</button>
        <input value={
    
    val} onChange={
    
    event => setValue(event.target.value)} />
      </div>
    </div>
  )
}

useCallback

  • 记忆函数,它计算返回一个缓存函数。
import React, {
    
     useState, useCallback, useEffect } from 'react';

const App = () => {
    
    

  const [count, setCount] = useState(1);
  const [val, setVal] = useState('');

  const callback = useCallback(() => {
    
    
    return count
  }, [count])

  return (
    <div>
      <h1>父组件:{
    
    count}</h1>
      <Child callback={
    
    callback} />
      <div>
        <button onClick={
    
    () => setCount(count + 1)}>+count</button>
        <input value={
    
    val} onChange={
    
    event => setVal(event.target.value)} />
      </div>
    </div>
  )
}

function Child({
    
     callback }) {
    
    
  const [count, setCount] = useState(() => callback())
  useEffect(() => {
    
    
    console.log(2222)
    setCount(callback())
  }, [callback]);
  return (
    <div>
      <hr />
      <div>子组件:{
    
    count}</div>
    </div>
  )
}

useRef

  • useRef 跟 createRef 类似,都可以用来生成对 DOM 对象的引用
import React, {
    
     useRef } from 'react'
const Ref = () => {
    
    
  const username = useRef()
  const login = () => {
    
    
    console.log(username.current.value);
  }
  return (
    <div>
      <div>
        <input type="text" ref={
    
    username} />
        <br />
        <br />
        <button onClick={
    
    login}>添加用户</button>
      </div>
    </div>
  )
}

useImperativeHandle

  • 使用它可以透传 Ref,因为函数组件没有实例,所以在默认自定义函数组件中不能使用ref属性,使用为了解决此问题,react提供了一个hook和一个高阶组件完帮助函数组件能够使用ref属性。
import React, {
    
     useRef, useEffect, useImperativeHandle, forwardRef } from "react"
function ChildInputComponent(props, ref) {
    
    
  const inputRef = useRef(null)
  // 穿透ref
  //useImperativeHandle(ref, () => inputRef.current)
  useImperativeHandle(ref, () => ({
    
    id:1,name:’aaa’}))
  return <input type="text" ref={
    
    inputRef} />
}
const ChildInput = forwardRef(ChildInputComponent)

function App() {
    
    
  const inputRef = useRef(null)
  const getValue = () => {
    
    
    console.log(inputRef.current.value)
  }

  return (
    <div>
      <ChildInput ref={
    
    inputRef} />
      <hr/>
      <button onClick={
    
    getValue}>获取一下数据</button>
    </div>
  )
}
	通过forwardRef完成父组件通过Ref来给子组件获取input的dom对象

useLayoutEffect

  • 大部分情况下,使用 useEffect 就可以帮我们处理组件的副作用,但是如果想要同步调用一些副作用,比如对 DOM 的操作,就需要使用 useLayoutEffect,useLayoutEffect 中的副作用会在 DOM 更新之后同步执行。
import React, {
    
     useState, useLayoutEffect, useEffect } from 'react'

const Layout = () => {
    
    
  const [value, setValue] = useState(0)
  useLayoutEffect(() => {
    
    
    const title = document.querySelector("#title")
    console.log("useLayoutEffect");
    setValue(title.innerHTML);
  })

  useEffect(() => {
    
    
    console.log("useEffect")
  })

  return (
    <div>
      <div><h1 id="title">hello</h1><h2>{
    
    value}</h2></div>
    </div>
  )
}

自定义hook

  • 定义自定义hook以use开头 + 函数名称,通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。
import React, {
    
    useState, useEffect} from 'react'
// 是否在线
function useOnline() {
    
    
  const [online, setOnline] = useState(navigator.onLine)

  useEffect(() => {
    
    
    const handlerOnline = () => setOnline(true)
    const handlerOffline = () => setOnline(false)

    window.addEventListener('online', handlerOnline, false)
    window.addEventListener('offline', handlerOffline, false)

    return () => {
    
    
      window.removeEventListener('online', handlerOnline, false)
      window.removeEventListener('offline', handlerOffline, false)
    }
  })
  return online
}

function App() {
    
    
  const online = useOnline()
  return (
      <div>
        {
    
    
          online ?
              <h3 style={
    
    {
    
    color: 'green'}}>在线</h3>
              :
              <h3>离线了 </h3>
        }
      </div>
  )
}

hooks与类组件的区别

在 Hooks 出现之前,典型的回答是类组件提供了更多的特性(比如生命周期,state等)。或是性能问题,Hooks 会因为在渲染时创建函数而变慢吗? 不会。在现代浏览器中,闭包和类的原始性能只有在极端场景下才会有明显的差别。引用React开发者 Dan Abramov 博客中的解释 “性能主要取决于代码的作用,而不是选择函数式还是类组件。在我们的观察中,尽管优化策略各有略微不同,但性能差异可以忽略不计”。

更根本的原因,心智模型(mental model)上的区别。

函数式组件更加“声明式”
在 Class 组件中,通常的写法是在生命周期检查 props.A 和 state.B,如果改变或者是符合某个条件就触发xxx副作用。在函数式组件中使用 Hooks 的写法是组件有xxx副作用,这个副作用依赖的数据是 props.A 和 state.B。从过去的命令式转变成了声明式的写法,Hooks 提供各种声明式的副作用 API (useEffect, useCallback),使得“生命周期”变成了一个“底层概念”,对开发者是无感知的,因此开发者更能够将精力聚焦在在更高的抽象层次上。

函数式组件更加“函数式”
从字面意义上来看,函数式组件肯定比类组件更“函数式”,从概念上来看,React 组件一直更像是函数,而 Hooks 则拥抱了函数,同时也没有牺牲 React 的精神原则。

hooks组件其实是降低了react开发的使用难度的,让新手可以在不使用class组件的情况下依然可以进行项目开发
可以不需要考虑this问题
可以不用使用高阶组件依然可以进行功能复用了

猜你喜欢

转载自blog.csdn.net/shadowfall/article/details/115387015