likes
comments
collection
share

React Hooks 全方位解析

作者站长头像
站长
· 阅读数 51

React 是一种非常流行的 JavaScript 库,而 React 中的 Hooks 则是 React 16.8 版本中的新特性,它使 React 组件的状态管理和逻辑重构变得更加简单和优雅。那么今天就来给大家聊聊 React 中的 Hooks,包括 useState、useEffect、useContext、useReducer、useCallback、useMemo、useRef 和 useImperativeHandle。

1. useState

useState 是 React 可以让函数式组件也可以拥有状态。useState 接收一个初始状态值,并返回一个包含初始状态值和更新状态值的数组。通过 useState,我们可以在函数式组件中定义和使用状态,并根据用户交互或其他事件来修改状态。来,让我们看一段示例:

import React, { useState } from 'react';
function Counter() {
  const [count, setCount] = useState(0);
   return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

在上面的例子中,我们定义了一个名为 count 的状态变量,并通过调用 useState 来初始化为 0。我们还定义了一个名为 setCount 的函数,用于更新 count 状态变量。在组件的渲染过程中,使用 count 变量展示页面上按钮被点击的次数,并通过调用 setCount 来更新 count 状态变量。

2. useEffect

useEffect 让函数式组件也可以处理类似于生命周期事件的副作用,例如获取数据、订阅事件、操作 DOM 等。我们可以使用 useEffect 实现组件的挂载、更新和卸载等生命周期的相关操作,也可以在 useEffect 中处理一些需要解绑的事件、网络请求等副作用。 useEffect 接收两个参数,第一个参数是需要执行的函数,第二个参数是一个数组,用于指定 useEffect 需要监听的状态变量数组。函数将在组件渲染后执行,并在组件被卸载时进行清理。依赖项数组用于告诉React什么时候应该重新运行副作用。 例如,我们可以通过以下方式在函数式组件中使用 useEffect:

import React, { useState, useEffect } from 'react';
 function Example() {
  const [count, setCount] = useState(0);
   useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);
   return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

在上面的例子中,我们定义了一个名为 count 的状态变量,并通过调用 useState 来初始化为 0。我们使用 useEffect 来监听 count 的变化,当 count 发生变化时,我们通过设置 document.title 实现动态的页面标题。在组件的渲染过程中,使用 count 变量展示页面上按钮被点击的次数,并通过调用 setCount 来更新 count 状态变量。

3. useContext

useContext 提供了一个让组件之间共享数据的方式,避免了层层传递 props 的麻烦。它允许我们在函数组件中轻松地访问 React Context 的值。它接收一个 Context 对象并返回当前 Context 值。就像在类组件中使用 this.context 一样,使用 useContext 可以访问在 Context.Provider 中定义的任何值。我们只需要在父组件中使用 createContext 创建一个上下文,然后通过 useContext 来在子组件中消费上下文中的数据即可。来看一段代码:

import React, { useContext } from 'react';
const MyContext = React.createContext('Hello');
function Parent() {
  return (
    <MyContext.Provider value="World">
      <Child />
    </MyContext.Provider>
  );
}
function Child() {
  const value = useContext(MyContext);
  return <div>{value}</div>;
}

在上面的示例中,我们定义了一个名为 MyContext 的上下文对象,并通过 createContext 函数创建了一个默认值为 “Hello” 的上下文。 在子组件 Child 中使用 useContext(MyContext) 来获取 MyContext 上下文中的数据,并在渲染过程中将其展示在页面上。 在父组件 Parent 中通过在 MyContext.Provider 组件中使用 value 来设置 MyContext 上下文的值为 “World” 。

4. useReducer

useReducer 可以用于处理具有复杂状态逻辑的组件。它的作用类似于 Redux 中的 reducer,但是它可以更轻量级地管理组件的局部状态。它接收一个 reducer 函数和一个初始状态,并返回一个包含状态和 dispatch 函数的元组。用于派发更新操作。使用方式如下:

import React, { useReducer } from 'react';
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 initialState = { count: 0 };
function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
   return (
    <div>
      <p>You clicked {state.count} times</p>
      <button onClick={() => dispatch({ type: 'increment' })}>
        Click me
      </button>
      <button onClick={() => dispatch({ type: 'decrement' })}>
        Click me
      </button>
    </div>
  );
}

在上面这段代码中,我们定义了一个 initialState 作为初始状态值,以及一个 reducer 函数来处理状态的变化。 接着,在 Counter 组件中使用 useReducer 将 initialState 和 reducer 函数传递给 useReducer,并返回了一个包含了状态值和 dispatch 函数的数组。 这个状态值可以在组件中直接使用,而 dispatch 函数可以接收一个 action 对象,用来触发 reducer 函数的执行以更新状态。

5. useCallback

useCallback 用于返回一个 memoized(记忆化)的回调函数,以避免在每次重新渲染组件时都创建新的函数。使用 useCallback 可以有效地优化 React 应用程序的性能。 useCallback 接收两个参数:回调函数和一个依赖项数组(可选)。只有当依赖项数组中的某个值发生变化时,useCallback 才会返回一个新的回调函数;否则,它将返回先前缓存的回调函数。咱来看一段示例代码:

import React, { useCallback } from 'react';
function Example({ onButtonClick }) {
  const handleClick = useCallback(() => {
    onButtonClick('Hello, world!');
  }, [onButtonClick]);
   return <button onClick={handleClick}>Click me!</button>;
}

在上面的示例中,我们将 handleClick 函数传递给了按钮的 onClick 属性。由于我们希望每次重新渲染组件时都不会创建新的函数,因此我们使用 useCallback 缓存了 handleClick 函数并将 onButtonClick 作为依赖项传递给 useCallback。这意味着只有当 onButtonClick 改变时,useCallback 才会返回一个新的函数。

6. useMemo

useMemo 用于返回一个 memoized(记忆化)值。与 useCallback 类似,使用 useMemo 可以避免在每次重新渲染组件时都重新计算值,从而有效地优化 React 应用程序的性能。一般来说,当计算一个值比较耗时时,可以使用 useMemo 来避免不必要的重复计算。 useMemo 接收两个参数:计算值的函数和一个依赖项数组(可选)。只有当依赖项数组中的某个值发生变化时,useMemo 才会重新计算并返回一个新的值;否则,它将返回先前缓存的值。

import React, { useMemo } from 'react';
function Example({ itemCount }) {
  const message = useMemo(() => {
    return `${itemCount} items`;
  }, [itemCount]);
  return <div>{message}</div>;
}

在上面的示例中,我们使用 useMemo 缓存消息字符串的计算结果并将 itemCount 作为依赖项传递给 useMemo。这意味着只有当 itemCount 改变时,useMemo 才会重新计算并返回一个新的消息字符串。

7. useRef

useRef 用于在函数式组件中保存和更新可变值。相对于 useState,useRef 保存的值可以在组件重新渲染时保持不变,并且更新这些值不会触发组件的重新渲染。 useRef 返回一个可变的 ref 对象,该对象具有一个 current 属性,该属性被初始化为传递给 useRef 的参数。ref 对象可以在组件的整个生命周期中使用,而无需重新声明或重新初始化变量。ref 对象的 current 属性可以被读取和更新,而不会触发组件的重新渲染。

import React, { useRef } from 'react';
function Example() {
  const inputRef = useRef(null);
  const handleClick = () => {
    inputRef.current.focus();
  };
  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleClick}>Focus Input</button>
    </div>
  );
}

在上面的示例中,我们使用 useRef 来创建一个 inputRef 对象,并将其分配给 input 元素的 ref 属性。然后,我们可以在 handleClick 函数内部使用 inputRef.current.focus() 来将焦点设置在输入框中。 除了保存和更新 DOM 元素的引用,useRef 还可以用于保存和更新任何可变值,例如计时器 ID、WebSocket 连接、或者其他需要在组件渲染周期内保持不变的值。

8. useImperativeHandle

useImperativeHandle 允许我们将特定方法向下传递到子组件以便在需要时调用它。也就是说可以让我们在函数式组件中控制父组件直接调用子组件中暴露出来的方法或属性。useImperativeHandle 需要与 forwardRef 一起使用,forwardRef 可以将父组件传递给子组件。具体来讲,useImperativeHandle 接受两个参数,一个 ref 和一个提供给该 ref 的回调函数,在回调函数中定义子组件向父组件暴露出的方法或属性,并返回一个值或函数,该值或函数可以从 ref 中访问。说起来挺绕口的,我们还是看代码吧:

import React, { forwardRef, useImperativeHandle } from 'react';
const Child = forwardRef((props, ref) => {
  const childRef = useRef();
  useImperativeHandle(ref, () => ({
    focusChild: () => {
      childRef.current.focus();
    },
    someValue: '我是子组件暴露出来的属性',
  }));
   return <input type="text" ref={childRef} />;
});
const Parent = () => {
  const childRef = useRef();
  const handleClick = () => {
    childRef.current.focusChild && childRef.current.focusChild();
  };
  return (
    <div>
      <Child ref={childRef} />
      <button onClick={handleClick}>Focus Input</button>
    </div>
  );
};
export default Parent;

在上面的示例中,我们创建了一个 Child 组件,用 useRef 来创建一个 childRef 对象,以便可以在回调函数中使用。在 useImperativeHandle 中,我们定义了 focusChild 方法和 someValue 属性,并将其返回给父组件。回调函数返回的对象将被与传递给 forwardRef 的 ref 对象合并。在 Parent 组件中,我们通过点击按钮的方式来调用子组件的 focusChild 方法,使输入框获得焦点。

React 中的主要 Hooks 就这几个,使用它们可以让代码更简洁、优雅和易于维护。当然,这些 Hooks 的应用场景远不止这些,大家可以在实际使用中不断探索它们的真正潜力。

转载自:https://juejin.cn/post/7230019548660580410
评论
请登录