react快速上手(二) Hooks【转载】

85 阅读7分钟

Hooks简介

产生的背景: React的组件形式,类组件和函数组件,React团队希望,组件复杂化,推荐使用函数组件,而不是类组件

目的: 解决函数组件中没有状态(state)、生命周期

优点:

  • 解决类组件中生命周期以及this指向问题
  • 解决业务逻辑难以拆分的问题
  • 使状态逻辑复用变得简单可行
  • 函数组件从设计思想上来看更加契合React的理念

常用的Hooks函数

1、State Hook

useState():状态钩子。纯函数组件没有状态,用于为函数组件引入state状态, 并进行状态数据的读写操作

语法、参数及返回值说明

const [xxx, setXxx] = React.useState(initValue) 
  • 参数:  第一次初始化指定的值在内部作缓存
  • 返回值:  包含2个元素的数组,第1个为内部当前状态值,第2个为更新状态值的函数

setXxx()2种写法:

  • setXxx(newValue) : 参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
  • setXxx(value => newValue) : 参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值

代码示例:计数器

import React,{ useState } from "react";

const NewCount = ()=> {
    const [ count,setCount ] = useState(0)
    addCount = ()=> {
        let newCount = count;
        setCount(newCount +=1)
    }
   return (
       <>
           <p> { count }</p>
           <button onClick={ addCount }>Count++</button>
       </>
   )
}
export default NewCount;

2、Context Hook

useContext():共享状态钩子作用就是可以做状态的分发,在React16.X以后支持,避免了react逐层通过Props传递数据。一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

使用语法和说明:

  1. 创建Context容器对象
const XxxContext = React.createContext()  
  1. 渲染子组件时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
<xxxContext.Provider value={数据}>
	<子组件/>
</xxxContext.Provider>
  1. 后代组件读取数据
const {} = useContext(XxxContext)

例如:A组件和B组件需要共享一个状态:

import React, { useContext } from "react";
const HookTest = ()=> {
    const AppContext = React.createContext();
    const A = ()=> {
        const { name } = useContext(AppContext)
        return (
            <p>
                我是A组件,我的名字是:{ name };
                <span>我是A的子标签:{ name }</span>
            </p>
        )
    }
    const B= ()=> {
        const { name } = useContext(AppContext);
        return (
            <p>我是B组件,名字是: { name }</p>
        )
    }
    return (
        <AppContext.Provider value={{ name: '张三'}}>
            <A />
            <B />
        </AppContext.Provider>
    )
}
export default HookTest;

3、Effect Hook

useEffect():副作用钩子用来更好的执行副作用操作(用于模拟类组件中的生命周期钩子),如异步请求等,在类组件中会把请求放在componentDidMount里面,在函数组件中可以使用useEffect()

使用语法和说明:

useEffect(() => { 
      // 在此可以执行任何带副作用操作
      return () => { // 在组件卸载前执行
        // 在此做一些收尾工作, 比如清除定时器/取消订阅等
      }
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行

参数、返回值说明:

  • useEffect()接受两个参数,第一个参数是要进行的异步操作,第二个参数是一个数组,用来给出Effect的依赖项,只要这个数组发生变化,useEffect()就会执行。
  • 当第二项省略不填时。useEffect()会在每次组件渲染时都会执行useEffect,只要更新就会执行。
  • 当第二项传 空数组[ ] 时,只会在组件挂载后运行一次。
  • useEffect()返回值可以是一个函数,在组件销毁的时候会被调用。清理这些副作用可以进行如取消订阅、清除定时器操作,类似于componentWillUnmount。

React中的副作用操作:

  • 发ajax请求数据获取
  • 设置订阅 / 启动定时器
  • 手动更改真实DOM

useEffect两个注意点:

  • React首次渲染和之后的每次渲染都会调用一遍useEffect函数,而之前要用两个生命周期函数分别表示 首次渲染(componentDidMonut) 和更新导致的重新渲染(componentDidUpdate)
  • useEffect中定义的函数的执行不会阻碍浏览器更新视图,也就是说这些函数时异步执行的,而componentDidMonut和componentDidUpdate中的代码都是同步执行的

总结:  可以把 useEffect Hook 看做如下三个函数的组合 :componentDidMount()、componentDidUpdate()、componentWillUnmount()

1、类似于componentDidMount的useEffect

import { useEffect } from 'react'

const Demo = () => {
  useEffcet(() => {
	console.log('类似于componentDidMount,通常在此处调用api获取数据')
  }, [])
}

export default Demo

2、类似于componentDidMount的useEffect

import { useEffect } from 'react'

const Demo = () => {
  useEffcet(() => {
	console.log('类似于componentDidMount,通常在此处调用api获取数据')
  }, [])
}

export default Demo

3、类似于componentDidUpdate的useEffect

import { useState,useEffect } from 'react'

const Demo = () => {
  const [count,setCount] = React.useState(0)
	
  useEffcet(() => {
	console.log('当count发生改变时,执行当前区域的代码')
  }, [count])
}

4、Reducer Hook

useReducer():Action钩子。在使用React的过程中,如遇到状态管理,一般会用到Redux。而React本身是不提供状态管理的。而useReducer() 提供了状态管理

语法格式:

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

它接受 reducer函数 和 状态的初始值 作为参数,返回一个数组,其中第一项为当前的状态值,第二项为发送action的dispatch函数

**代码示例:使用useReducer()实现一个计数器

import  { useReducer } from "react";
const HookReducer = ()=> {
    const reducer = (state,action)=> {
        if (action.type === 'add') {
            return {
                ...state,
                count: state.count + 1
            }
        }else {
            return state
        }
    }
    const addCount = ()=> {
        dispatch({
            type: 'add'
        })
    }
    const [state,dispatch ] = useReducer(reducer,{count: 0})
    return (
        <>
            <p>{state.count}</p>
            <button onClick={ addCount }>useReducer</button>
        </>
    )
}
export default HookReducer;

5、Ref Hook

userRefef():Ref Hook可以在函数组件中存储、查找组件内的标签或任意其它数据

语法格式:

const refContainer = useRef()

useRef返回一个可变的ref对象,useRef接受一个参数绑定在返回的ref对象的current属性上,返回的ref对象在整个生命周期中保持不变。

作用:保存标签对象,功能与React.createRef()一样

代码示例:input上绑定一个ref,使得input在渲染后自动焦点聚焦

import{ useRef,useEffect} from "react";
const RefComponent = () => {
    let inputRef = useRef(null);
    useEffect(() => {
        inputRef.current.focus();
    })
    return (
        <input type="text" ref={inputRef}/>
    ) 
}

6、Memo Hook

useMemo(): 主要用来解决使用React hooks产生的无用渲染的性能问题

语法格式:

const cacheSomething = useMemo(create,deps)
  • create:第一个参数为一个函数,函数的返回值作为缓存值
  • deps: 第二个参数为一个数组,存放当前 useMemo 的依赖项,在函数组件下一次执行的时候,会对比 deps 依赖项里面的状态,是否有改变,如果有改变重新执行 create ,得到新的缓存值。
  • cacheSomething:返回值,执行 create 的返回值。如果 deps 中有依赖项改变,返回的重新执行 create 产生的值,否则取上一次缓存

useMemo应用场景:

  • 可以缓存 element 对象,从而达到按条件渲染组件,优化性能的作用。
  • 如果组件中不期望每次 render 都重新计算一些值,可以利用 useMemo 把它缓存起来。
  • 可以把函数和属性缓存起来,作为 PureComponent 的绑定方法,或者配合其他Hooks一起使用

7、Callback Hook

useCallback(): 主要是为了性能的优化

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

可以认为是对依赖项的监听,接受一个回调函数和依赖项数组。

useCallback会返回一个函数的memoized(记忆的)值。 该回调函数仅在某个依赖项改变时才会 在依赖不变的情况下,多次定义的时候,返回的值是相同的

代码示例

import {useState,useCallback} from "react";

const CallbackComponent = () => {
    let [count, setCount] = useState(1);
    let [num, setNum] = useState(1);

    const memoized = useCallback(() => {
        return num;
    }, [count])
    console.log("记忆:", memoized());
    console.log("原始:", num);
   return (
        <>
            <button onClick={() => {setCount(count + 1)}}> count+</button>
            <button onClick={() => {setNum(num + 1)}}> num+</button>
        </>
    )
}
export default CallbackComponent

自定义Hooks

  • 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook
  • 可以封装状态,能够更好的实现状态共享

自定义hooks可以说成是一种约定而不是功能。当一个函数以use开头并且在函数内部调用其他hooks,那么这个函数就可以成为自定义hooks 代码示例

import { useState,useEffect } from "react";
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('郭靖')}}>郭靖</button>
            <button onClick={ ()=> { changeName('黄蓉')}}>黄蓉</button>
        </>
    )
}
export default PersonPage;

转载原文: blog.csdn.net/weixin_4565…