首页 >web前端 >css教程 >React钩子:深切口

React钩子:深切口

Joseph Gordon-Levitt
Joseph Gordon-Levitt原创
2025-03-11 10:34:20432浏览

React Hooks: The Deep Cuts

Hooks是可复用的函数,允许您在不编写类的情况下使用状态和其他功能(例如生命周期方法等)。Hook函数使我们能够使用函数组件“钩入”React状态生命周期,从而允许我们操作函数组件的状态,而无需将其转换为类组件。

React在16.8版本中引入了Hooks,并从那时起不断添加更多Hooks。一些Hooks比其他Hooks更常用和流行,例如useEffectuseStateuseContext。如果您使用React,我相信您一定用过这些Hooks。

但我感兴趣的是鲜为人知的React Hooks。虽然所有React Hooks都各有特色,但我真正想向您展示五个Hooks,因为它们可能不会出现在您的日常工作中——或者也许会,了解它们会赋予您一些额外的超能力。

目录

  • useReducer
  • useRef
  • useImperativeHandle
  • useMemo
  • useCallback
  • 总结

useReducer

useReducer Hook是一个类似于其他Hook的状态管理工具。具体来说,它是useState Hook的替代方案。

如果您使用useReducer Hook来更改两个或多个状态(或操作),则无需单独操作这些状态。该Hook会跟踪所有状态并集体管理它们。换句话说:它管理和重新渲染状态更改。与useState Hook不同,在处理复杂项目中的许多状态时,useReducer更容易。

使用场景

useReducer可以帮助降低处理多个状态的复杂性。当您发现自己需要集体跟踪多个状态时,可以使用它,因为它允许您将组件的状态管理和渲染逻辑视为单独的关注点。

语法

useReducer接受三个参数,其中一个参数是可选的:

  • 一个reducer函数
  • initialState
  • 一个init函数(可选)
const [state, dispatch] = useReducer(reducer, initialState)
const [state, dispatch] = useReducer(reducer, initialState, initFunction) // 使用可选的第三个参数进行初始化的情况

示例

以下示例是一个包含文本输入、计数器和按钮的界面。与每个元素交互都会更新状态。请注意,useReducer允许我们一次定义多个情况,而不是单独设置它们。

import { useReducer } from 'react';
const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count   1 };
    case 'DECREMENT':
      return { ...state, count: state.count - 1 };
    case 'USER_INPUT':
      return { ...state, userInput: action.payload };
    case 'TOGGLE_COLOR':
      return { ...state, color: !state.color };
    default:
      throw new Error();
  }
}

function App() {
  const [state, dispatch] = useReducer(reducer, { count: 0, userInput: '', color: false })

  return (
    <main classname="App App-header" style="{{" backgroundcolor: state.color :>
      <input type="text" onchange="{(e)"> dispatch({ type: 'USER_INPUT', payload: e.target.value })} />
      <br><br><p style="{{" margin:>{state.count}</p>
      <button onclick="{()"> dispatch({ type: 'DECREMENT' })}>-</button>
      <button onclick="{()"> dispatch({ type: 'INCREMENT' })}> </button>
      <button onclick="{()"> dispatch({ type: 'TOGGLE_COLOR' })}>Color</button>
      <br><br><p style="{{" margin:>{state.userInput}</p>
    </main>
  );
}
export default App;

从上面的代码中,您可以注意到我们能够轻松地在reducer(switch-case)中管理多个状态,这展示了useReducer的优势。在具有多个状态的复杂应用程序中工作时,这就是它提供的强大功能。

useRef

useRef Hook用于在元素上创建引用,以便访问DOM。但不仅如此,它还返回一个具有.current属性的对象,该属性可以在组件的整个生命周期中使用,允许数据持久化而不会导致重新渲染。因此,useRef值在渲染之间保持不变;更新引用不会触发重新渲染。

使用场景

当您想要:

  • 使用存储的可变信息操作DOM。
  • 访问子组件(嵌套元素)中的信息。
  • 将焦点设置到元素上。

可以使用useRef Hook。它在存储应用程序中的可变数据而不导致重新渲染时最有用。

语法

useRef只接受一个参数,即初始值

const newRefComponent = useRef(initialValue);

示例

在这里,我使用了useRefuseState Hook来显示应用程序在文本输入中键入时重新渲染更新状态的次数。

import './App.css'

function App() {
  const [anyInput, setAnyInput] = useState(" ");
  const showRender = useRef(0);
  const randomInput = useRef(null); // 注意这里需要设置null

  const toggleChange = (e) => {
    setAnyInput(e.target.value);
    showRender.current  ;
  }
  const focusRandomInput = () => {
    if (randomInput.current) { // 添加判断,防止null引用错误
      randomInput.current.focus();
    }
  }

  return (
    <div classname="App">
      <h3>Amount Of Renders: {showRender.current}</h3>
      <input type="text" ref="{randomInput}" onchange="{toggleChange}">
      <button onclick="{focusRandomInput}">Click To Focus On Input</button>
    </div>
  );
}

export default App;

请注意,在文本字段中键入每个字符都会更新应用程序的状态,但不会触发完全重新渲染。

useImperativeHandle

您知道子组件如何访问从父组件传递给它们的功能吗?父组件通过props传递这些功能,但这种传递在某种意义上是“单向的”,因为父组件无法调用子组件中的函数。

那么,useImperativeHandle使父组件能够访问子组件的函数成为可能。

这是如何工作的?

  • 在子组件中定义一个函数。
  • 在父组件中添加一个ref。
  • 我们使用forwardRef,允许将定义的ref传递给子组件。
  • useImperativeHandle通过ref公开子组件的函数。

使用场景

当您希望父组件受子组件中更改的影响时,useImperativeHandle效果很好。因此,诸如更改焦点、递增和递减以及模糊元素的情况可能是您发现自己需要使用此Hook的情况,以便相应地更新父组件。

语法

useImperativeHandle(ref, createHandle, [dependencies])

示例

在此示例中,我们有两个按钮,一个在父组件中,一个在子组件中。单击父按钮可以从子组件检索数据,从而允许我们操作父组件。它的设置方式是单击子按钮不会将任何内容从父组件传递到子组件,以帮助说明我们如何以相反的方向传递内容。

// Parent component
import React, { useRef } from "react";
import ChildComponent from "./childComponent";
import './App.css';

function ParentComponent() {
  const controlRef = useRef(null);
  return (
    <div>
      <button onclick="{()"> {
        if (controlRef.current) {
          controlRef.current.controlPrint();
        }
      }}>Parent Box</button>
      <childcomponent ref="{controlRef}"></childcomponent>
    </div>
  );
}
export default ParentComponent;
// Child component
import React, { forwardRef, useImperativeHandle, useState } from "react";

const ChildComponent = forwardRef((props, ref) => {
  const [print, setPrint] = useState(false);
  useImperativeHandle(ref, () => ({
    controlPrint() {
      setPrint(!print);
    },
  }));

  return (
    <div>
      <button>Child Box</button>
      {print && <p>I am from the child component</p>}
    </div>
  );
});

export default ChildComponent;

输出

...(此处应包含示例输出截图或描述)

useMemo

useMemo是最不常用但最有趣的React Hooks之一。它可以提高性能并减少延迟,尤其是在应用程序中的大型计算中。怎么会这样呢?每次组件的状态更新和组件重新渲染时,useMemo Hook都会阻止React重新计算值。

您会看到,函数会响应状态更改。useMemo Hook接受一个函数并返回该函数的返回值。它缓存该值以防止花费额外的工作来重新渲染它,然后在其中一个依赖项发生更改时返回它。

此过程称为记忆化,它有助于通过记住先前请求的值来提高性能,以便可以再次使用它而无需重复所有这些计算。

使用场景

最佳用例将是任何时候您都在处理繁重的计算,您希望存储该值并在后续状态更改中使用它。它可以带来不错的性能提升,但过度使用它可能会产生完全相反的效果,从而占用应用程序的内存。

语法

useMemo(() => {
  // 代码在此处
}, [])

示例

单击按钮时,此小型程序会指示数字是偶数还是奇数,然后对该值进行平方。我在循环中添加了许多零以增加其计算能力。它在几秒钟内返回该值,并且由于useMemo Hook而仍然运行良好。

// UseMemo.js
import React, { useState, useMemo } from 'react'

function Memo() {
  const [memoOne, setMemoOne] = useState(0);
  const incrementMemoOne = () => { setMemoOne(memoOne   1) }
  const isEven = useMemo(() => {
    let i = 0;
    while (i < 1000000000) { i   } //增加计算量
    return memoOne % 2 === 0;
  }, [memoOne]);
  const squaredNumber = useMemo(() => {
    let i = 0;
    while (i < 1000000000) { i   } //增加计算量
    console.log("squared the number");
    return memoOne * memoOne;
  }, [memoOne]);

  return (
    <div>
      <h1>{memoOne}</h1>
      <button onClick={incrementMemoOne}>Increment</button>
      <p>Is Even: {isEven ? 'Yes' : 'No'}</p>
      <p>Squared Number: {squaredNumber}</p>
    </div>
  );
}

export default Memo;

输出

...(此处应包含示例输出截图或描述)

useMemo有点像useCallback Hook,但不同之处在于useMemo可以存储来自函数的记忆化值,而useCallback存储并返回函数本身。

useCallback

useCallback Hook是另一个有趣的Hook,上一节是对其功能的剧透。

正如我们刚刚看到的,useCallback的工作方式与useMemo Hook类似,因为它们都使用记忆化来缓存某些内容以供以后使用。useMemo将函数的计算存储为缓存值,而useCallback存储并返回函数。

使用场景

useMemo一样,useCallback是一种不错的性能优化,因为它存储并返回记忆化的回调及其任何依赖项,而无需重新渲染。

语法

const getMemoizedCallback = useCallback(
  () => { doSomething() }, []
);

示例

import React, { useCallback, useState } from "react";
import CallbackChild from "./UseCallback-Child";
import "./App.css"

export default function App() {
  const [toggle, setToggle] = useState(false);
  const [data, setData] = useState("I am a data that would not change at every render, thanks to the useCallback");
  const returnFunction = useCallback(
    (name) => { return data   name; }, [data]
  );
  return (
    <div>
      <button onclick="{()"> {
        setToggle(!toggle);
      }}>
        {/* Click To Toggle */}
      </button>
      {toggle && <h1>Toggling me no longer affects any function</h1>}
      <callbackchild returnfunction="{returnFunction}"></callbackchild>
    </div>
  );
}

// 子组件

import React, { useEffect } from "react";

function CallbackChild({ returnFunction }) {
  useEffect(() => {
    console.log("FUNCTION WAS CALLED");
  }, [returnFunction]);
  return <p>{returnFunction(" Hook!")}</p>;
}
export default CallbackChild;

输出

...(此处应包含示例输出截图或描述)

总结

就是这样!我们刚刚查看了五个我认为经常被忽视的非常方便的React Hooks。与许多这样的综述一样,我们只是触及了这些Hooks的表面。它们各自都有自己的细微之处和注意事项,在使用它们时需要考虑。但希望您对它们是什么以及何时比您可能更经常使用的其他Hook更适合有一个很好的高级概念。

充分理解它们的最佳方法是实践。因此,我鼓励您在您的应用程序中练习使用这些Hooks以更好地理解它们。为此,您可以通过查看以下资源来更深入地了解:

<code>- Intro to React Hooks (Kingsley Silas)
- Hooks at a Glance (React documentation)
- Hooks Cheatsheet (Ohans Emmanuel)
- The Circle of a React Lifecycle (Kingsley Silas)
- Hooks of React Router (Agney Menon)
- Testing React Hooks With Enzyme and React Testing Library (Kingsley Silas)</code>

以上是React钩子:深切口的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn