您现在的位置是:首页 >技术杂谈 >React中的Hooks介绍 -- 未完待续...网站首页技术杂谈
React中的Hooks介绍 -- 未完待续...
React中的Hooks介绍
1.Hooks是什么?
“hooks” 直译是 “钩子”,它并不仅是 react,甚至不仅是前端界的专用术语,而是整个行业所熟知的用语。通常指:系统运行到某一时期时,会调用被注册到该时机的回调函数。
以前:React 一直都提倡使用函数组件,但是有时候需要使用 state 或者其他一些功能时,只能使用类组件,因为函数组件没有实例,没有生命周期函数,只有类组件才有。
为什么要用Hooks?
Hooks 是 React 16.8 新增的特性,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性.
如果你在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其它转化为 class。现在你可以直接在现有的函数组件中使用 Hooks
凡是 use 开头的 React API 都是 Hooks
useContext
:
作用:在函数组件中访问 React 上下文。
解决了什么问题:在函数组件中无法使用 contextType
和 this.context
的问题。
代码案例:
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={theme}>I'm a themed button</button>;
}
特性:允许在函数组件中访问上下文。
优点:简化了上下文访问,使得代码更加简洁。
缺点:与类组件相比,函数组件无法订阅多个上下文。
useReducer
:
作用:在函数组件中处理复杂的状态逻辑。
解决了什么问题:在函数组件中无法使用类似 Redux 的状态管理的问题。
代码案例:
import React, { 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();
}
}
function Counter() {
const [state, dispatch]= useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
特性:允许在函数组件中处理复杂的状态逻辑。
优点:使得状态管理更加可预测和可维护。
缺点:可能增加代码的复杂性。
useCallback
:
作用:返回一个记忆化版本的回调函数。
解决了什么问题:避免在每次渲染时都创建一个新的回调函数,导致不必要的重新渲染。
代码案例:
import React, { useState, useCallback } from 'react';
function ExpensiveComponent({ onClick, count }) {
console.log('Component rerendered');
return <button onClick={onClick}>{count}</button>;
}
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<ExpensiveComponent onClick={handleClick} count={count} />
</div>
);
}
特性:通过记忆化回调函数,避免不必要的组件重新渲染。
优点:提高性能。
缺点:需要小心处理依赖项。
useMemo
:
作用:返回一个记忆化的值。
解决了什么问题:避免在每次渲染时都重新计算昂贵的操作。
代码案例:
import React, { useMemo, useState } from 'react';
function calculateExpensiveValue(num) {
console.log('Calculating expensive value');
return num * 2;
}
function App() {
const [count, setCount] = useState(0);
const expensiveValue = useMemo(() => {
return calculateExpensiveValue(count);
}, [count]);
return (
<div>
<p>Expensive value: {expensiveValue}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
特性:通过记忆化值,避免不必要的计算。
优点:提高性能。
缺点:需要小心处理依赖项。
useRef
:
作用:获取 DOM 元素的引用或者存储可变值。
解决了什么问题:在函数组件中无法使用实例变量的问题。
代码案例:
import React, { useRef, useEffect } from 'react';
function App() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return (
<div>
<input ref={inputRef} />
</div>
);
}
特性:允许在函数组件中访问和操作 DOM 元素。
优点:简化了 DOM 操作。
缺点:需要小心处理,以免引发不必要的副作用。
8.useImperativeHandle
:
作用:自定义通过 ref 暴露给父组件的实例值。
解决了什么问题:在使用 forwardRef
时,控制通过 ref 暴露给父组件的实例值。
代码案例:
import React, { useRef, useImperativeHandle, forwardRef } from 'react';
function CustomInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
}));
return <input ref={inputRef} />;
}
const CustomInputWithRef = forwardRef(CustomInput);
function App() {
const inputRef = useRef();
return (
<div>
<CustomInputWithRef ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus input</button>
</div>
);
}
特性:自定义通过 ref 暴露给父组件的实例值。
优点:增加了组件封装性和可控性。
缺点:增加了代码的复杂性。
useLayoutEffect
:
作用:在 DOM 变更之后,同步触发副作用函数。
解决了什么问题:避免在某些情况下,使用 useEffect
导致的不必要的闪烁问题。
代码案例:
import React, { useState, useLayoutEffect, useRef } from 'react';
function App() {
const [color, setColor] = useState('red');
const divRef = useRef();
useLayoutEffect(() => {
divRef.current.style.backgroundColor = color;
}, [color]);
return (
<div>
<div ref={divRef}>I am a colored div</div>
<button onClick={() => setColor('blue')}>Change color</button>
</div>
);
}
特性:在 DOM 变更之后,同步触发副作用函数。
优点:避免了不必要的闪烁问题。
缺点:可能会阻塞浏览器渲染,导致性能下降。
这些是 React Hooks 的主要功能和用途。它们在不同的场景下解决了许多问题,简化了代码,提高了性能。在使用 Hooks 时,需要关注它们的依赖项,以避免不必要的副作用。
在 React 中主要是 Hooks不应该放在条件语句
在 React 中,有一些东西是不应该放在条件语句(如 if
或 else
)中的。主要是 Hooks,包括但不限于 useState
、useEffect
、useContext
等。Hooks 必须始终遵循一致的顺序,因为 React 依赖于调用顺序来正确地处理它们。将 Hooks 放在条件语句中可能导致它们的调用顺序不一致,从而导致不可预测的行为。
例如,以下代码是错误的:
import React, { useState, useEffect } from 'react';
function MyComponent(props) {
const [count, setCount] = useState(0);
if (props.someCondition) {
useEffect(() => {
console.log('This is a bad practice!');
}, []);
}
// ...
}
在这个例子中,useEffect
被放在了一个 if
语句中。这意味着它可能会在某些渲染过程中被跳过,导致 React 无法正确地追踪 Hooks 的调用顺序。
为了避免这个问题,你应该始终在组件的顶层使用 Hooks,并保持它们的调用顺序不变。如果需要根据条件执行副作用,你可以将条件放在 useEffect
的回调函数内部,而不是在 Hook 周围:
import React, { useState, useEffect } from 'react';
function MyComponent(props) {
const [count, setCount] = useState(0);
useEffect(() => {
if (props.someCondition) {
console.log('This is the correct way to do it!');
}
}, []);
// ...
}
在这个修复后的示例中,useEffect
始终会被调用,但副作用函数内部的逻辑仅在满足 props.someCondition
时才会执行。这样可以确保 React 正确地处理 Hooks,同时仍然允许你根据条件执行特定逻辑。