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问题
可以不用使用高阶组件依然可以进行功能复用了