掌握 ReactJS Hooks:现代 Web 开发综合指南
作者:互联网
ReactJS 钩子的历史
在 ReactJS Hooks 之前,开发人员必须使用类组件或高阶组件 (HOC) 来管理状态、生命周期方法和其他 React 功能。虽然类组件和 HOC 很有效,但它们通常会导致代码复杂且可重用性较低。引入钩子是为了简化状态管理并减少创建 React 组件所需的样板代码量。钩子是允许开发人员“挂钩”到 React 生命周期和状态管理系统的函数。它们提供了一种更具可组合性和可重用性的方式来管理 React 应用程序中的状态和逻辑。
Hooks 于 2019 年初首次推出。从那时起,它们已成为现代 Web 开发的重要工具。Hooks 现在广泛用于 React 应用程序,许多开发人员认为它们是 Web 开发中的游戏规则改变者。
在钩子之前做出反应
这是管理状态的类组件的示例:
import React , { Component } from 'react' ; class Counter extends Component { constructor ( props ) { super (props); 这个。状态= { 计数: 0 , }; 这个。增量=这个。递增。绑定(这个); } 增量(){ 这个。设置状态({ 计数:这个。状态。计数+ 1 , }); } render ( ) { return ( <div> <p>Count: {this.state.count}</p> <button onClick={this.increment}>Increment</button> </div> ); } }
在此示例中,我们使用类组件来管理计数状态变量。我们在构造函数中将状态初始化为 0,然后在点击“增量”按钮时使用增量方法更新计数状态。
下面是一个管理状态的 HOC 示例:
import React , { Component } from 'react' ; function withCounter ( WrappedComponent ) { return class extends Component { constructor ( props ) { super (props); 这个。状态= { 计数: 0 , }; 这个。增量=这个。递增。绑定(这个); } 增量() { 这个. setState ({ count : this . state . count + 1 , }); } render ( ) { return ( <WrappedComponent count={this.state.count} increment={this.increment} {...this.props} /> ); } }; } function Counter ( props ) { return ( <div> <p>Count: {props.count}</p> <button onClick={props.increment}>增量</button> </div> ); } const CounterWithState = withCounter (计数器);
在此示例中,我们使用 HOC 来管理计数状态变量。withCounter 函数将一个组件作为参数并返回一个管理计数状态的新组件。然后在应用程序中使用 CounterWithState 组件来呈现具有添加的状态管理功能的 Counter 组件。
虽然类组件和 HOC 很有效,但它们通常会导致代码复杂且可重用性较低。ReactJS Hooks 提供了一种更具可组合性和可重用性的方式来管理 React 应用程序中的状态和逻辑。
ReactJS 钩子如何工作
ReactJS Hooks 的工作原理是允许开发人员在功能组件中使用状态和其他 React 特性。钩子是以前缀“use”开头的函数。每个挂钩都有特定的用途和用例。以下是 React 中必须知道的钩子:
使用状态
useState 钩子允许开发人员管理功能组件中的状态。它返回一个包含两个元素的数组:当前状态值和更新状态的函数。
下面是使用 useState 挂钩管理计数状态变量的示例:
导入 React , { useState } from 'react' ; function Counter ( ) { const [count, setCount] = useState ( 0 ); 函数 增量(){ setCount(计数+ 1); } return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> </div> ); }
在此示例中,我们使用 useState 挂钩来管理计数状态变量。我们用 0 值初始化状态,然后在单击“增量”按钮时使用 setCount 函数更新计数状态。
使用效果
useEffect hook 允许开发人员处理功能组件中的副作用和生命周期方法。它有两个参数:一个在组件安装或更新时运行的函数,以及一个控制效果何时运行的依赖项数组。
下面是使用 useEffect 挂钩从 API 获取数据的示例:
导入 React , { useState, useEffect } from 'react' ; function DataFetcher ( ) { const [data, setData] = useState ( null ); useEffect ( () => { fetch ( ' https://api.example.com/data' ) . then ( response => response.json ()) . then ( data => setData (data)) . catch ( error =>控制台 . 错误(错误)); }, []); 返回( <div> {data ? ( <ul> {data.map(item => ( <li key={item.id}>{item.name}</li> ))} </ul> ) : ( <p>加载数据...</p> )} </div> ); }
在此示例中,我们使用 useEffect 挂钩在组件安装时从 API 获取数据。我们用 null 值初始化数据状态,然后使用 setData 函数用获取的数据更新状态。
使用上下文
useContext hook 允许开发人员在组件之间共享数据,而无需进行 prop 钻取。它以上下文对象作为参数并返回上下文的当前值。
下面是使用 useContext 挂钩将主题对象传递给多个组件的示例:
导入 React , { useState, useContext } from 'react' ; 常量 ThemeContext =反应。createContext({ 颜色:'蓝色', 背景:'白色', }); function App ( ) { const [theme, setTheme] = useState ({ color : 'blue' , background : 'white' , }); 返回( <ThemeContext.Provider value={theme}> <button onClick={() => setTheme({ color: 'red', background: 'black' })}> 改变主题 </button> <ComponentA /> <ComponentB /> </ThemeContext.Provider> ); } function ComponentA ( ) { const theme = useContext ( ThemeContext ); return <div style={{ color: theme.color, background: theme.background }}>组件A</div>; } function ComponentB ( ) { const theme = useContext ( ThemeContext ); 返回<div style={{ color: theme.color, background: theme.background }}>组件B</div>; }
在这个例子中,我们使用 React 提供的 createContext 方法定义了一个 ThemeContext。ThemeContext 对象定义了一个蓝色和白色背景的默认主题。
App 组件使用 useState 钩子来定义可以通过单击按钮进行更新的主题状态对象。App 组件还呈现两个子组件,ComponentA 和 ComponentB,它们包装在 ThemeContext.Provider 组件中。ThemeContext.Provider 组件将主题状态对象的当前值提供给它的子组件。
ComponentA 和 ComponentB 组件使用 useContext 挂钩从 ThemeContext 中检索当前主题对象。然后他们使用主题对象来设置各自 div 元素的颜色和背景。
当用户点击“更改主题”按钮时,调用 setTheme 函数来更新主题状态对象。这会导致 ComponentA 和 ComponentB 组件使用更新后的主题对象重新呈现,从而导致其 div 元素的颜色和背景发生变化。
较少使用的挂钩
使用参考
useRef 钩子用于创建对 DOM 元素的引用或在组件渲染中持久存在的值。useRef 钩子返回一个可变的 ref 对象,可以在不触发重新渲染的情况下进行更新。下面是使用 useRef 挂钩创建对输入元素的引用的示例:
import React , { useRef } from 'react' ; 函数 InputWithFocusButton ( ) { const inputRef = useRef ( null ); 函数 handleClick ( ) { inputRef. 当前。焦点(); } return ( <div> <input type="text" ref={inputRef} /> <button onClick={handleClick}>焦点输入</button> </div> ); }
在此示例中,我们使用 useRef 挂钩创建对输入元素的引用。然后,当单击“焦点输入”按钮时,我们使用 inputRef 对象将焦点设置在输入元素上。
使用备忘录
useMemo 钩子用于记忆一个值,这意味着该值仅在其依赖项发生变化时才计算。useMemo 钩子对于不需要在每次渲染时重新计算的昂贵计算很有用。下面是使用 useMemo 钩子计算阶乘的示例:
import React , { useMemo } from 'react' ; function Factorial ( { n } ) { const factorial = useMemo ( () => { let result = 1 ; for ( let i = 1 ; i <= n; i++) { result *= i; } return result; }, [ n]); 返回<div>{阶乘}</div>; }
在此示例中,我们使用 useMemo 挂钩来计算数字的阶乘。useMemo 钩子只会在 n 的值发生变化时重新计算阶乘。
使用回调
useCallback 钩子用于记忆一个函数,这意味着该函数仅在其依赖项发生变化时才创建。useCallback 钩子对于优化使用回调函数作为 props 的组件的性能很有用。下面是使用 useCallback 挂钩记忆点击处理程序的示例:
import React , { useCallback } from 'react' ; function Button ( { onClick } ) { const handleClick = useCallback ( () => { onClick ( 'clicked' ); }, [onClick]); return <button onClick={handleClick}>点我</button>; }
在此示例中,我们使用 useCallback 挂钩来记忆点击处理程序。只有在 onClick 函数发生变化时才会创建 handleClick 函数。
使用Reducer
useReducer 钩子用于管理功能组件中的状态。useReducer 钩子类似于 useState 钩子,但它提供了对状态更新方式的更多控制。下面是使用 useReducer 挂钩管理计数器状态变量的示例:
导入 React , { useReducer } from 'react' ; function reducer ( state, action ) { switch (action.type ) { case ' increment' : return { count : state. 计数+ 1 }; case 'decrement' : return { count : 状态。计数- 1 }; 默认:抛出新错误(); } }函数计数器 ( ) { const [state, dispatch] = useReducer (reducer, { count : 0 }); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}>Increment</button> <button onClick={ () => dispatch({ type: 'decrement' })}>Decrement</button> </div> ); }
在此示例中,我们使用 useReducer 挂钩来管理计数器状态变量。我们定义了一个 reducer 函数,它接受一个状态和一个动作并返回一个新状态。然后我们使用 useReducer 钩子将状态初始化为 { count: 0 } 并提供 reducer 函数来处理状态更新。
使用过渡
useTransition 钩子是 React 的一个相对较新的附加功能,它允许您在安装或卸载组件时向组件添加流畅的动画。它提供了一种方法来协调组件的状态变化与由这些变化触发的动画。
useTransition 挂钩有两个参数:一个配置对象和一个布尔值,指示组件当前是否处于“挂起”状态。配置对象包含三个属性:timeoutMs,这是动画的持续时间,以毫秒为单位;enter,是一个布尔值,表示组件是挂载还是卸载;和 leave,这是一个布尔值,指示组件是否正在卸载。
下面是一个示例,说明如何使用 useTransition 挂钩在组件挂载时向其添加淡入动画:
导入 React , { useState, useTransition } from 'react' ; function App ( ) { const [show, setShow] = useState ( false ); const [startTransition, isPending] = useTransition ({ timeoutMs : 500 , enter : true , leave : false , }); function handleClick ( ) { startTransition ( () => { setShow ( ( prevShow) => !prevShow); }); } return ( <div> <button onClick={handleClick}>Toggle Component</button> {isPending ? ( <p>Loading...</p> ) : ( show && <div style={{ opacity: 1, transition: 'opacity 0.5s' }}>Component</div> )} </div> ); }
在此示例中,我们定义了一个名为“show”的状态变量,用于确定是否应显示组件。我们还使用 useTransition 钩子定义了一个转换,它的超时时间为 500 毫秒,“进入”值为 true,“离开”值为 false。
当用户单击“切换组件”按钮时,将调用 handleClick 函数。此函数调用 startTransition 函数,该函数采用设置“show”状态变量值的回调函数。startTransition 函数管理组件安装或卸载时的动画。
useTransition 挂钩返回的 isPending 变量用于在组件处于“挂起”状态时显示加载消息。show 变量用于在组件安装时有条件地显示带有淡入动画的组件。
自定义挂钩
自定义钩子是一种在 React 组件中重用有状态逻辑的方法。它们允许您将公共逻辑提取并封装到一个单独的函数中,该函数可以跨多个组件使用。自定义钩子遵循与常规钩子相同的规则,但它们可以有自己的状态、效果和其他特性。
要创建自定义挂钩,您可以(但不强制)使用与创建常规挂钩相同的规则。自定义钩子的名称应以“use”一词开头,以表明它是一个钩子。自定义挂钩还应返回一个数组,其中包含状态值和更新状态的函数。
以下是管理表单输入状态的自定义挂钩示例:
从 “反应”导入{useState} ; function useFormInput ( initialValue ) { const [value, setValue] = useState (initialValue); function handleChange ( event ) { setValue ( event.target.value ) ; }返回[值,handleChange];} function App ( ) { const [name, setName] = useFormInput ( '' ); 函数句柄提交(事件 ) { 事件。防止默认(); 警报(`你好,${name}!`); } return ( <form onSubmit={handleSubmit}> <label> 名称: <input type="text" value={name} onChange={setName} /> </label> <button type="submit">提交</按钮> </表单> ); }
在这个例子中,我们定义了一个名为“useFormInput”的自定义钩子,它接受一个初始值作为参数。该挂钩返回一个包含当前值的数组和一个在输入字段更改时更新值的函数。
我们在 App 组件中使用“useFormInput”挂钩来管理输入字段的值。我们将“name”状态变量和“useFormInput”挂钩返回的“setName”函数作为道具传递给输入字段。
当用户提交表单时,会调用 handleSubmit 函数,它会显示带有“name”状态变量值的警告消息。
下面是另一个自定义钩子的例子,它管理一个带有延迟的计数器状态:
从 'react'导入{ useState, useEffect } ; function useCounter ( initialValue, delay ) { const [count, setCount] = useState (initialValue); useEffect ( () => { const interval = setInterval ( () => { setCount ( ( prevCount ) => prevCount + 1 ); }, delay); return () => clearInterval (interval); }, [delay]) ; 返回计数; } 函数 App ( ) { 常量计数 = useCounter ( 0 , 1000 ); 返回<p>计数:{count}</p>; }
在这个例子中,我们定义了一个名为“useCounter”的自定义钩子,它接受一个初始值和一个延迟作为参数。该挂钩返回当前计数值,使用 setInterval 函数每“延迟”毫秒将其递增 1。
我们在 App 组件中使用“useCounter”钩子来管理计数状态。我们将初始值 0 和 1000 毫秒(1 秒)的延迟传递给“useCounter”挂钩。计数值显示在段落元素中。
使用 ReactJS 钩子的最佳实践
以下是使用 ReactJS 钩子确保您的代码可维护、可扩展和安全的一些最佳实践:
- 将挂钩保持在组件的顶层——挂钩应该在组件的顶层调用,而不是在循环、条件或其他嵌套函数内。这确保以一致的顺序调用挂钩并且正确更新状态。
- 仅在功能组件中使用钩子——钩子应该只在功能组件中使用,而不应在类组件或其他非功能组件中使用。这确保代码是一致的,并遵循 React 的“组合优于继承”的范例。
- 避免副作用——钩子应该用于管理状态和生命周期事件,而不是执行 API 调用或 DOM 操作等副作用。应该使用 useEffect 钩子来管理副作用,它允许您安全有效地执行副作用。
- 使用 useState 挂钩管理状态——useState 挂钩是功能组件中管理状态的主要挂钩。它允许您声明状态变量并使用函数更新它们。这使得以声明和可预测的方式管理和更新状态变得容易。
- 使用 useEffect 挂钩管理生命周期事件——useEffect 挂钩是管理功能组件中生命周期事件的主要挂钩。它允许您在组件呈现后或某些状态变量发生变化时执行副作用,例如 API 调用或 DOM 操作。
- 使用自定义挂钩重用逻辑——自定义挂钩允许您跨多个组件重用有状态逻辑。它们是封装复杂逻辑并使其可重用和可维护的强大方法。
- 使用 useRef 挂钩访问 DOM 元素 - useRef 挂钩允许您直接在功能组件中访问 DOM 元素。这对于执行 DOM 操作或访问 DOM 元素的属性很有用。
- 使用 useContext 挂钩管理全局状态 - useContext 挂钩允许您跨多个组件管理全局状态。它允许您将数据和函数向下传递到组件树中,而不必将它们传递到每个中间组件。
通过遵循这些最佳实践,您可以确保您的 ReactJS 代码是可维护的、可扩展的和安全的,并且它遵循软件开发的最佳实践。