ReactJS - getSnapshotBeforeUpdate() 方法
众所周知,React 中的每个组件都有自己的生命周期,这意味着它们在我们的项目中运行时会经历不同的阶段。React 提供了内置方法来控制这些过程。
现在让我们来看看 getSnapshotBeforeUpdate() 方法。假设我们正在使用 React 创建一个网页和一个定期接收消息的聊天组件。现在,我们不希望每次收到新消息时滚动位置都会发生变化。让用户忘记对话中的位置。这就是 getSnapshotBeforeUpdate 的作用所在。
简单来说,React 会在修改网页之前调用此函数。它允许我们的组件在任何潜在变化发生之前从页面捕获一些信息,例如用户滚动到的位置。
语法
getSnapshotBeforeUpdate(prevProps, prevState)
参数
prevProps − 这些是更改之前存在的属性。可以将它们与 this.props 进行比较以查看新内容。
prevState − 这是更改之前的先前状态。要确定更改,请将其与 this.state 进行比较。
返回值
我们应该返回任何类型的快照值或 null。我们返回的值将作为第三个参数发送到 componentDidUpdate。
示例
示例 1
让我们构建一个使用 getSnapshotBeforeUpdate 函数的小型 React 应用。在此示例中,我们将构建一个简单的聊天应用程序,其中有新消息,我们希望保存滚动位置。
import React, { Component } from 'react'; class App extends Component { constructor(props) { super(props); this.state = { messages: [ { id: 1, text: 'Hello!' }, { id: 2, text: 'How are you?' }, ], newMessage: '', }; this.chatWindowRef = React.createRef(); } handleInputChange = (event) => { this.setState({ newMessage: event.target.value }); }; handleSendMessage = () => { const { messages, newMessage } = this.state; // 创建新的消息对象 const newMessageObj = { id: messages.length + 1, text: newMessage, }; // 使用新消息更新状态 this.setState({ messages: [...messages, newMessageObj], newMessage: '', }); }; getSnapshotBeforeUpdate(prevProps, prevState) { // 检查是否有新消息被添加 if (prevState.messages.length < this.state.messages.length) { const chatWindow = this.chatWindowRef.current; return chatWindow.scrollHeight - chatWindow.scrollTop; } return null; } componentDidUpdate(prevProps, prevState, snapshot) { // 如果有快照,请调整滚动位置 if (snapshot !== null) { const chatWindow = this.chatWindowRef.current; chatWindow.scrollTop = chatWindow.scrollHeight - snapshot; } } render() { const { messages, newMessage } = this.state; return ( <div> <div ref={this.chatWindowRef} style={{ height: '200px', overflowY: 'scroll', border: '1px solid #ccc', padding: '10px' }} > {/* Display messages */} {messages.map((message) => ( <div key={message.id}>{message.text}</div> ))} </div> {/* Input for new message */} <div> <input type="text" value={newMessage} onChange={this.handleInputChange} /> <button onClick={this.handleSendMessage}>Send Message</button> </div> </div> ); } } export default App;
输出

在此应用程序中 −
ChatApp 组件保留一个消息列表和一个用于添加新消息的表单。
函数 getSnapshotBeforeUpdate 用于查明是否有新消息添加并记录当前滚动位置。
如果添加了新消息,componentDidUpdate 将更新滚动位置。
聊天窗口包含一个可滚动区域用于显示消息。
示例 2
让我们创建一个简单的 React 应用程序,用户可以在其中输入数字并执行基本的算术运算。以下是代码 −
import React, { Component } from 'react'; import './App.css'; class CalculatorApp extends Component { constructor(props) { super(props); this.state = { result: 0, num1: '', num2: '', operator: '+', }; } handleNumChange = (event, numType) => { const value = event.target.value; this.setState({ [numType]: value, }); }; handleOperatorChange = (event) => { this.setState({ operator: event.target.value, }); }; handleCalculate = () => { const { num1, num2, operator } = this.state; // 将输入值转换为数字 const number1 = parseFloat(num1); const number2 = parseFloat(num2); // 根据所选运算符进行计算 let result = 0; switch (operator) { case '+': result = number1 + number2; break; case '-': result = number1 - number2; break; case '*': result = number1 * number2; break; case '/': result = number1 / number2; break; default: break; } // 使用结果更新状态 this.setState({ result, }); }; render() { const { result, num1, num2, operator } = this.state; return ( <div className='App'> <div> <input type="number" value={num1} onChange={(e) => this.handleNumChange(e, 'num1')} /> <select value={operator} onChange={this.handleOperatorChange}> <option value="+">+</option> <option value="-">-</option> <option value="*">*</option> <option value="/">/</option> </select> <input type="number" value={num2} onChange={(e) => this.handleNumChange(e, 'num2')} /> <button onClick={this.handleCalculate}>Calculate</button> </div> <div> <strong>Result:</strong> {result} </div> </div> ); } } export default CalculatorApp;
输出

在此代码中,我们创建了一个简单的计算器应用,用户可以在其中输入两个数字,选择一个算术运算符,并在单击"计算"按钮后查看结果。
示例 3
让我们创建一个小型 React 应用,让用户输入任务并将其标记为已完成。添加新任务时,我们将使用 getSnapshotBeforeUpdate 函数滚动到任务列表的底部。以下是该应用的代码 −
import React, { Component } from 'react'; class TaskListApp extends Component { constructor(props) { super(props); this.state = { tasks: [], newTask: '', }; this.taskListRef = React.createRef(); } handleInputChange = (event) => { this.setState({ newTask: event.target.value }); }; handleAddTask = () => { const { tasks, newTask } = this.state; // 创建一个新的任务对象 const newTaskObj = { id: tasks.length + 1, text: newTask, completed: false, }; // 使用新任务更新状态 this.setState({ tasks: [...tasks, newTaskObj], newTask: '', }); }; getSnapshotBeforeUpdate(prevProps, prevState) { // 检查是否有新任务被添加 if (prevState.tasks.length < this.state.tasks.length) { const taskList = this.taskListRef.current; return taskList.scrollHeight - taskList.scrollTop; } return null; } componentDidUpdate(prevProps, prevState, snapshot) { if (snapshot !== null) { const taskList = this.taskListRef.current; taskList.scrollTop = taskList.scrollHeight - snapshot; } } handleToggleComplete = (taskId) => { const updatedTasks = this.state.tasks.map((task) => task.id === taskId ? { ...task, completed: !task.completed } : task ); this.setState({ tasks: updatedTasks, }); }; render() { const { tasks, newTask } = this.state; return ( <div> <div ref={this.taskListRef} style={{ height: '200px', overflowY: 'scroll', border: '1px solid #ccc', padding: '10px' }} > {/* Display tasks */} {tasks.map((task) => ( <div key={task.id} style={{ textDecoration: task.completed ? 'line-through' : 'none' }}> <input type="checkbox" checked={task.completed} onChange={() => this.handleToggleComplete(task.id)} /> {task.text} </div> ))} </div> {/* Input for new task */} <div> <input type="text" value={newTask} onChange={this.handleInputChange} /> <button onClick={this.handleAddTask}>Add Task</button> </div> </div> ); } } export default TaskListApp;
输出

在此应用中,用户可以输入任务、将其标记为已完成,并在列表中查看任务。添加新任务时,函数 getSnapshotBeforeUpdate 用于滚动到任务列表的底部。
注意事项
如果定义了 shouldComponentUpdate 并返回 false,则 React 将不会调用 getSnapshotBeforeUpdate。
目前函数组件没有与 getSnapshotBeforeUpdate 直接等效的功能。如果我们需要此功能,则必须使用类组件。
总结
因此,我们已经了解了 getSnapshotBeforeUpdate() 函数的工作机制。并且我们还创建了一个小应用来展示该函数的用法。该组件可以包含到我们的 React 应用程序中,以展示 getSnapshotBeforeUpdate 如何在添加新内容时有助于维持滚动位置。