ReactJS - 组件 API
ReactJS - 组件交互:State、Refs 和重新渲染
Section titled “ReactJS - 组件交互:State、Refs 和重新渲染”本教程将探讨 React 中管理组件数据和行为的关键概念:使用 useState hook 更新 state,理解 React 如何重新渲染组件,以及使用 useRef hook 直接访问 DOM 元素。我们将重点关注函数组件,这是现代 React 开发的标准方式,它简化了许多方面,包括自动处理 this 上下文。
使用 useState 管理 State
Section titled “使用 useState 管理 State”useState hook 是为函数组件添加 state(状态)的基本方法。它返回一个对:当前的 state 值和一个更新该值的函数。调用这个更新函数将触发组件及其子组件的重新渲染。
更新 state 时,特别是数组或对象,关键是要将 state 视为不可变的(immutable)。这意味着您应该创建一个新的数组或对象,而不是直接修改现有对象。这有助于 React 优化更新并防止出现细微的 bug。
import React, { useState } from 'react';
function App() { // Initialize 'data' state as an empty array // 初始化 'data' state 为一个空数组 const [data, setData] = useState([]);
// Handler to add a new item to the 'data' array // 添加新项到 'data' 数组的处理器 const updateStateHandler = () => { const item = "Item added via useState!"; // Create a new array with the old items and the new item // 创建一个包含旧项和新项的新数组 setData(prevData => [...prevData, item]); };
return ( <div> <button onClick={updateStateHandler}>ADD ITEM TO STATE</button> <h4>State Array:</h4> <ul> {data.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> </div> );}
export default App;我们使用一个空数组初始化 data。每次点击按钮时,updateStateHandler 被调用,它使用 setData 函数向 data 数组添加一个新字符串。然后组件重新渲染以显示更新后的列表。如果点击三次,列表将显示三个项。
触发重新渲染(及 forceUpdate 的考虑)
Section titled “触发重新渲染(及 forceUpdate 的考虑)”在现代 React 中,当组件的 state 或 props 改变时,它们会自动重新渲染。通常不鼓励直接手动重新渲染,例如类组件中的 forceUpdate(),因为这往往表明设计存在问题或对 React 的响应式数据流理解不足。
如果您发现自己在没有直接 state 或 prop 变化的情况下需要重新渲染组件(例如,当依赖于外部的非响应式数据源时,这是一种反模式),您可能会想寻找 forceUpdate 的等效方法。在函数组件中,一种常见的变通方法是使用一个虚拟的 state 变量,通过更新它来触发重新渲染。但是,请始终首先尝试根据组件的 props 和 state 来建模其渲染逻辑。
原始示例使用 forceUpdate() 来显示一个新的随机数。以下是如何使用 useState 实现类似的结果,这是 React 的惯用方式:
import React, { useState } from 'react';
function App() { const [randomNumber, setRandomNumber] = useState(Math.random());
const generateNewRandomNumber = () => { setRandomNumber(Math.random()); // Update state, triggering a re-render // 更新 state,触发重新渲染 };
return ( <div> <button onClick={generateNewRandomNumber}>NEW RANDOM NUMBER</button> <h4>Random Number: {randomNumber}</h4> </div> );}
export default App;每次点击按钮都会更新 randomNumber state,导致组件重新渲染并显示新的随机数。这是 React 处理 UI 更新的声明式方式。
使用 useRef 访问 DOM 元素
Section titled “使用 useRef 访问 DOM 元素”虽然 React 抽象了直接的 DOM 操作,但有时您需要直接与 DOM 元素交互(例如,管理焦点、触发动画或集成第三方 DOM 库)。useRef hook 提供了一种访问底层 DOM 元素的方法。
ReactDOM.findDOMNode() 在旧的类组件中使用,但现在不建议使用,并且很大程度上被 useRef 替代。useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(或 null)。当在 JSX 中将 ref 属性传递给元素时,当组件挂载时,React 会将其 .current 属性设置为对应的 DOM 元素。
import React, { useRef } from 'react';// For React 18, the root API is used for rendering// 对于 React 18,使用 root API 进行渲染// import ReactDOM from 'react-dom/client';// No direct DOM manipulation in this example, but refs can be used for it.// 在此示例中没有直接的 DOM 操作,但 ref 可用于此目的。
function App() { const divRef = useRef(null); // Create a ref // 创建一个 ref
const changeDivColor = () => { if (divRef.current) { // Accessing the DOM element directly // 直接访问 DOM 元素 divRef.current.style.color = 'green'; divRef.current.textContent = 'NODE (Color Changed!)'; } };
return ( <div> <button onClick={changeDivColor}>CHANGE NODE COLOR</button> {/* Attach the ref to the div element */} {/* 将 ref 附加到 div 元素 */} <div ref={divRef} id="myDiv">NODE</div> </div> );}
export default App;点击按钮时,changeDivColor 直接操作 divRef.current 引用的 DOM 节点,将其文本颜色更改为绿色并更新其内容。虽然这是可能的,但请记住,对于基于应用逻辑更改样式和内容的情况,通常首选通过 state 进行声明式更新。
关于 this 上下文和事件处理程序的一点说明
Section titled “关于 this 上下文和事件处理程序的一点说明”在旧的类组件中,您通常需要手动 .bind(this) 用于事件处理程序,或使用箭头函数来确保正确的 this 上下文。使用函数组件和 Hooks,这不再是一个问题。函数组件中定义的事件处理程序自然地捕获其词法作用域中的变量(包括来自 useState 的 state setter)。
现代 React 开发
Section titled “现代 React 开发”React 已经发生了显著变化,函数组件和 Hooks 成为标准。这种方法通常能写出更简洁、更易读、更易维护的代码。虽然仍然支持类组件,但新的开发通常倾向于使用 Hooks 进行 state 管理、side effects、context 和 refs。