Hooks出现的动机
组件复用状态逻辑很难
React没有提供可复用性行为“附加”到组件的途径,在写类组件的时候,一个类是一个闭包并且state在组件间传递并不怎么友好,虽然可以使用props和高阶组件来解决,但是这样会组件的结构更麻烦。如果你在 React DevTools 中观察过 React 应用,你会发现由 providers,consumers,高阶组件,render props 等其他抽象层组成的组件会形成“嵌套地狱”。
复杂的组件可读性差
如果一个组件的逻辑很复杂,可能会导致每个生命周期包含一些不相关的代码。而 Hook 可以将组件中相互关联的部分拆分的更细,使得代码更简洁,结果也更加可预测。
Class 组件现存的一些问题
例如 this 的复杂性以及生命周期函数的不确定性等,都使得 class 组件成为一个难以使用的存在。因此,使用 Hook 来实现一些和生命周期函数类似的功能,有助于解决这些问题。
什么是Hooks
在函数组件中把React的状态和生命周期等这些特性钩入进入,这就是React的Hook。
内置Hooks总览
useState
usEffect
useContext
useReducer
useCallback
useMemo
useRef
useImperativeHandle
useLayoutEffect
useState
声明状态变量,用来储存和管理数据,用法是传入一个初始数据,当函数执行后会返回两个值,一个是当前状态的属性,一个是修改状态的方法,如下代码
import {useState} from 'react'
export default function Test() {
const [count,setCount]=useState(0)
return (
{count}
<button onClick={()=>{setCount(count+1)}}>click
)
}
useEffect
useEffect的作用是指定一个副作用函数,每次组件渲染时,该函数就自动执行一次,他接受两个参数,第一个是副作用函数,第二个是一个数组,表示依赖项,当我们并不想每次渲染都执行副作用函数时就可以传入第二个参数。如果第二个参数是一个空数组,就表明副效应参数没有任何依赖项。因此,副效应函数这时只会在组件加载进入 DOM 后执行一次,后面组件重新渲染,就不会再次执行。
import {useEffect,useState} from 'react'
export default function Test() {
const [count,setCount]=useState(0)
useEffect(()=>{
console.log(count);
})
return (
{count}
<button onClick={()=>{setCount(count+1)}}>click
)
}
还是引用上面的例子,每次点击之后,控制台都会打印count的值,但传入一个空数组后,只会打印一次。
另外,useEffect有返回值,在组件卸载时,会清理副效应。
useContext
React 的 Context API 是一种在应用程序中深入传递数据的方法,而无需手动一个一个在多个父子孙之间传递 prop。当咱们需要的只是传递数据时,它可以作为像Redux这样的工具的一个很好的替代。简而言之,useContext可以解决多层组件传值的问题。
这是父组件
import React, { createContext } from "react";
import Child from "./Child";
export const MyContext = React.createContext();export default function Father() {
const obj = {
name: 'Bob', age: 18
}
return (
<MyContext.Provider value={obj}>
</MyContext.Provider>
)
}
这是子组件
import React, { useContext } from "react";
import { MyContext } from "./Father";
export default function Child() {
console.log(MyContext);
const obj=useContext(MyContext)
console.log(obj);
return (
{obj.name}
{obj.age}
)
}
最终页面会呈现响应数据。
useReducer
useReducer 是 useState 的替代方案。当 useState 不能很好的满足需要的时候,useReducer 可能会解决我们的问题。
他接受三个参数,第一个参数 reducer 是函数 (state, action) => newState,接受当前的 state 和操作行为。第二个参数 initialArg 是状态初始值。第三个参数 init 是懒惰初始化函数。
import React, { useReducer } from "react";
const initState = {
amount: 0
};
const reducer = (state, action) => {
switch (action.type) {
case "add":
return {
amount: state.amount + 1
};
case "minus":
return {
amount: state.amount - 1
};
case "reset":
return {
amount: 0
};
default:
return {
amount: 0
};
}
};const UseReducerDemo = () => {
const [state, dispatch] = useReducer(reducer, initState);return (
The amount is:
{state.amount}
<button
onClick={() => {
dispatch({ type: "add" });
}}
>
Add
<button
onClick={() => {
dispatch({ type: "minus" });
}}
>
minus
<button
onClick={() => {
dispatch({ type: "reset" });
}}
>
reset
);
};
useCallback
它的作用是缓存一个函数,以确保在组建重新渲染是不会创建新的回调函数,从而减少不必要的性能开销。它通常与React.memo一起使用,以避免不必要的组件重新渲染。
它的用法与useEffect有点相似
export default function 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 onClick={() => {setNum(num + 1)}}> num+
</>
)
}
打开控制台发现,只有当count值发生改变时,menmoized函数的执行结果会发生改变
useMemo
useMemo和uesCallback有点像,不过它的返回值不是函数,而是一个值,当依赖项发生改变时,才会重新计算值
import { useMemo } from 'react';
export default function MyComponent(props) {
const { a, b } = props;const expensiveValue = useMemo(() => {
// 计算昂贵的值
return a + b;
}, [a, b]);return (
expensiveValue: {expensiveValue}
);
}
useRef
它用于获取组件中的DOM元素、组件或函数,常用于页面中DOM元素的操作、函数的调用等。