关于React Hooks(下)
create by db on 2023-8-18 16:28:44
Recently revised in 2023-8-18 16:28:44闲时要有吃紧的心思,忙时要有悠闲的趣味
前言
正文
一、什么是Hook
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
在 React 中,useState 以及任何其他以“use”开头的函数都被称为 Hook。
Hooks 优势
能优化类组件的三大问题
- 能在无需修改组件结构的情况下复用状态逻辑(自定义 Hooks )
- 能将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)
- 副作用的关注点分离:副作用指那些没有发生在数据向视图转换过程中的逻辑,如 ajax 请求、访问原生dom 元素、本地持久化缓存、绑定/解绑事件、添加订阅、设置定时器、记录日志等。以往这些副作用都是写在类组件生命周期函数中的。而 useEffect 在全部渲染完毕后才会执行,useLayoutEffect 会在浏览器 layout 之后,painting 之前执行。
注意事项
- 只能在函数内部的最外层调用 Hook,不要在循环、条件判断或者子函数中调用
- 只能在 React 的函数组件中调用 Hook,不要在其他 JavaScript 函数中调用
五、其他 Hooks
useContext
useContext可以帮助我们跨越组件层级直接传递变量,实现数据共享 Context的作用就是对它所包含的组件树提供全局共享数据的一种技术。
useContext作用
React 的 Context 指的是组件上下文的意思,用于简化子组件跨多层中间组件和上层组件的通信问题。
如果用 props 一个个往下传,直接人传晕了。而且以后如果中间组件又套多了几个,又要补上对应 props,这不利于组件的通用化。
比如你需要子组件可以修改上层组件的状态,传入了一个 setState 方法,此时 Context 是非常不错的方案。
useContext使用
useContext的使用可以分为3步:
- 使用 const Context = React.createContext(); 创建上下文
- 使用 <Context.Provider> 圈定作用域
- 在作用域内使用 const { setState } = useContext(Context); 来使用上写文
// 引入React及useState
import React, { useState, useContext } from "react";
// 声明Context
const Context = React.createContext();
// 祖先组件
// 使用 <Context.Provider></Context.Provider> 包裹起来并绑定数据
function Ancestor () {
const [state, setState] = useState("state");
return (
<Context.Provider value={{ state, setState }}>
<div>{state}</div>
<Son />
</Context.Provider>
);
}
// 儿子组件
// 仅作层级
function Son () {
return (
<div>
<Grandson />
</div>
);
}
// 孙子组件
// 使用 setState 更新数据
function Grandson () {
const { setState } = useContext(Context);
const onButtonClick = () => {
setState("孙子知道了");
};
return (
<div>
<button onClick={onButtonClick}>孙子请回答</button>
</div>
);
}
export default Ancestor;
useMemo
作用:
useMemo用于优化渲染性能。
useMemo 会接收一个箭头函数包裹的回调函数和依赖项数组,然后返回回调函数的计算结果。当依赖项数组中的某个值发生变化时,useMemo 会重新计算回调函数。如果依赖项没有发生变化,useMemo 会返回上一次计算的结果,这样可以避免不必要的计算。
如下,只有在a或者b发生改变的时候,value的值才会重新计算。
const value = useMemo(() => {
return caculateFunction (a, b)
}, [a, b])
使用场景:
当存在一个昂贵的计算操作,且该操作的输入值在多次渲染之间不会发生变化时,应当使用useMemo 。 示例代码如下:
// 引入React及useMemo
import React, { useState, useMemo } from "react";
// 父组件
function Father () {
const [num1, setNum1] = useState(1);
const [num2, setNum2] = useState(1);
const onButtonClick1 = () => {
setNum1(num1 + 1)
};
const onButtonClick2 = () => {
setNum2(num2 + 1)
};
return (
<div>
<button onClick={onButtonClick1}>
更新数据1
</button>
<button onClick={onButtonClick2}>
更新数据2
</button>
<Son num1={num1} num2={num2} /></div>
);
}
// 儿子组件
function Son (props) {
let { num1, num2 } = props
// computeNum1 会在num1, num2更新后重新计算
const computeNum1 = (num1) => {
console.log('计算num1')
return num1 * 10 * 10 * 10 * 10 * 10 * 10
}
// useMemo 只会在num1更新后重新计算
const value1 = useMemo(() => {
console.log('useMemo计算num1')
return num1 * 10 * 10 * 10 * 10 * 10 * 10
}, [num1])
return (
<div>
<div >{computeNum1(num1)}</div>
<div >{value1}</div>
</div>
);
}
export default Father;
useCallback
作用:
useCallback 是一个允许你在多次渲染中缓存函数的 React Hook。
同样它也会接受两个参数,callback和依赖项, 当依赖数组中的值发生变化时,useCallback 会返回一个新的函数实例。否则,它将返回上一次创建的函数实例。
useCallback 通常结合React.Memo进行使用,来避免不必要的渲染
const childFucntion = useCallback(() => {
action()
}, [a, b])
使用场景:
有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。 示例代码如下:
import React,{ useState, useCallback,useEffect } from "react";
const Child2 = ({ callback }) => {
//使用useEffect来监听callback是否发生变化, ”child“ 会被打印出来
useEffect(() => {
console.log("in child useEffect");
}, [callback]);
//这里我们观察子组件是否再次渲染,"render child" 也会被打印出来
console.log("render child");
return <button onClick={callback}>改变布尔值</button>;
}
// 使用 React.memo,解决组件重新渲染的问题
const Child1 = React.memo(Child2);
const Father = () => {
const [logic, setLogic] = useState(false);
const [count, setCount] = useState(0);
const handleClickCount = () => {
setCount(count + 1);
};
//每次App重新渲染handleCallback都是不同的函数
const handleCallback = () => {
setLogic((pre) => !pre);
}
//useCallback 包裹函数,可以防止handleCallback每次被重新创建
const handleCallbackWithUseCallback = useCallback(handleCallback, []);
return (
<div>
<p>数值: {count}</p>
<p>布尔值: {logic.toString()}</p>
<button onClick={handleClickCount}>增加数值</button>
<Child1 callback={handleCallbackWithUseCallback} />
</div>
);
}
export default Father;
总结
- useCallback在它的依赖不变的状况下,总是返回相同的函数
- useCallback可以和React.memo 结合在一起使用来避免不必要的渲染。
- 虽然useCallback是一种优化的手段。但是这种优化,也会产生调用useCallback所用的资源消耗,同时加上useCallback也会增加代码的复杂度。对于简单的组件来说,滥用useCallback不仅不会优化,反而会适得其反的。
useReducer
作用:
useReducer 是 React 中一种用于管理组件状态的钩子函数。它的作用是将组件的状态分解为多个值,并提供一种可预测、可控的状态更新方式,从而使得代码更加清晰易懂。
useReducer 是 useState 的替代方案,useState 能做到的事,它都能做到,甚至做得更好。
用法:
useReducer 的定义如下:
const [state, dispatch] = useReducer(reducer, initialState);
useReducer 接受两个参数:
reducer
:一个接受当前状态和动作(action)的 函数 ,返回 新的状态 。在 reducer 中,可以根据 action 的类型来修改状态。initialState
:状态的初始值。
useReducer 的返回值是一个数组,数组中包含两个元素:
state
:当前的状态值。dispatch
:一个用于触发状态更新的函数。当 dispatch 被调用时,会触发 reducer 函数,并传递当前状态和 action 作为参数。
示例如下:
import React, { useReducer } from 'react';
// 定义 reducer 函数,用于控制状态的变化
function reducer (state, action) {
switch (action.type) {
case 'INCREMENT': // 当 action 的类型为 'INCREMENT' 时,状态加1
return state + 1;
case 'DECREMENT': // 当 action 的类型为 'DECREMENT' 时,状态减1
return state - 1;
default: // 默认情况下,返回原来的状态
return state;
}
}
function Counter () {
// 使用 useReducer 钩子函数来管理状态
const [count, dispatch] = useReducer(reducer, 0);
return (
<div>
{/* 在组件中渲染状态值 */}
<p>Count: {count}</p>
{/* 点击按钮时,使用 dispatch 函数来触发状态更新 */}
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
</div>
);
}
export default Counter
使用场景:
在 React 中,useState、useReducer 和 Redux 都是常用的状态管理工具。它们各自有自己的优缺点,适用于不同的场景。
- useState
- useState 是 React 提供的一种基础的状态管理工具。它的使用非常简单,只需要提供初始值即可,而且可以通过 setState 函数来更新状态。 与 useReducer 相比,useState 的优点在于它更加简单易用,适用于一些简单的状态管理场景。但缺点在于,当状态变得比较复杂时,使用 useState 可能会导致代码冗长、难以维护。
- useReducer
- useReducer 是 React 提供的另一种状态管理工具。它通过 reducer 函数来控制状态的变化,从而使得状态更新更加可控、可预测。 与 useState 相比,useReducer 的优点在于它可以管理更加复杂的状态,并且状态更新更加可控、可预测。缺点在于,相对于 useState 而言,使用 useReducer 需要写更多的代码,对于初学者来说可能会有一定的学习曲线。
- Redux
- Redux 提供了一种集中式的状态管理方式。在 Redux 中,所有的状态都存储在一个中央 store 中,通过 action 和 reducer 来控制状态的变化。 与 useState 和 useReducer 相比,Redux 的优点在于它可以方便地管理跨组件的状态,并且提供了一种可预测、可控的状态更新方式。缺点在于,相对于 useState 和 useReducer 而言,Redux 需要写更多的代码,并且可能会增加一些额外的复杂度。
了解这些工具各自适用的应用场景,对我们选择合适的状态管理工具很有帮助。通过他们各自的优缺点在特定的场景中使用合适的状态管理工具能极大提高我们的开发效率。因此,不要盲目的使用 useReducer。
总结
未完待续
参考文档
- React官网
- 面试官求你别再问我hook了 | 掘金-食困症患者
- 终于搞懂 React Hooks了!!!!! | 掘金-西门吹喵
- 浅谈React钩子函数 useMemo 和 useCallback | 掘金-蚌埠张学友
- 理解React的useCallback钩子🪝基础知识 | 掘金-catlin)
- act开发者必备技能:掌握useReducer的基础和应用 | 掘金-码上花甲)
后记:Hello 小伙伴们,如果觉得本文还不错,记得点个赞或者给个 star,你们的赞和 star 是我编写更多更丰富文章的动力!GitHub 地址
文档协议
db 的文档库 由 db 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/danygitgit上的作品创作。
本许可协议授权之外的使用权限可以从 creativecommons.org/licenses/by… 处获得。