您现在的位置是:首页 >技术杂谈 >React中的Hooks介绍 -- 未完待续...网站首页技术杂谈

React中的Hooks介绍 -- 未完待续...

临在❀ 2024-06-17 11:19:32
简介React中的Hooks介绍 -- 未完待续...

1.Hooks是什么?

“hooks” 直译是 “钩子”,它并不仅是 react,甚至不仅是前端界的专用术语,而是整个行业所熟知的用语。通常指:系统运行到某一时期时,会调用被注册到该时机的回调函数。

以前:React 一直都提倡使用函数组件,但是有时候需要使用 state 或者其他一些功能时,只能使用类组件,因为函数组件没有实例没有生命周期函数,只有类组件才有

为什么要用Hooks?

Hooks 是 React 16.8 新增的特性,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性.

如果你在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其它转化为 class。现在你可以直接在现有的函数组件中使用 Hooks

凡是 use 开头的 React API 都是 Hooks

  1. useContext:

作用:在函数组件中访问 React 上下文。

解决了什么问题:在函数组件中无法使用 contextTypethis.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>;
}

特性:允许在函数组件中访问上下文。

优点:简化了上下文访问,使得代码更加简洁。

缺点:与类组件相比,函数组件无法订阅多个上下文。

  1. 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>
);
}

特性:允许在函数组件中处理复杂的状态逻辑。

优点:使得状态管理更加可预测和可维护。

缺点:可能增加代码的复杂性。

  1. 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 暴露给父组件的实例值。

优点:增加了组件封装性和可控性。

缺点:增加了代码的复杂性。

  1. 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 中,有一些东西是不应该放在条件语句(如 ifelse)中的。主要是 Hooks,包括但不限于 useStateuseEffectuseContext 等。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,同时仍然允许你根据条件执行特定逻辑。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。