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;
      
      // Create a new message object
      const newMessageObj = {
         id: messages.length + 1,
         text: newMessage,
      };
      
      // Update the state with the new message
      this.setState({
         messages: [...messages, newMessageObj],
         newMessage: '',
      });
   };   
   getSnapshotBeforeUpdate(prevProps, prevState) {
      // Check if new messages are being added
      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 there's a snapshot, adjust the scroll position
      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;

输出

send message

在这个应用中:

  • 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;
      
      // Convert input values to numbers
      const number1 = parseFloat(num1);
      const number2 = parseFloat(num2);
      
      // Perform calculation based on the selected operator
      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;
      }
      
      // Update the state with result
      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;

输出

calculate result

在此代码中,我们创建了一个简单的计算器应用程序,用户可以在其中输入两个数字,选择一个算术运算符,然后单击“计算”按钮后查看结果。

示例 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;
      
      // Create a new task object
      const newTaskObj = {
         id: tasks.length + 1,
         text: newTask,
         completed: false,
      };
      
      // Update the state with the new task
      this.setState({
         tasks: [...tasks, newTaskObj],
         newTask: '',
      });
   };
   
   getSnapshotBeforeUpdate(prevProps, prevState) {
      // Check if new tasks are being added
      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;

输出

adding task

在这个应用中,用户可以输入任务,将它们标记为已完成,并在列表中查看任务。当添加新任务时,getSnapshotBeforeUpdate 函数用于滚动到任务列表的底部。

注释

  • 如果定义了 shouldComponentUpdate 并返回 false,则 React 将不会调用 getSnapshotBeforeUpdate。

  • 目前,函数组件没有 getSnapshotBeforeUpdate 的直接等效项。如果我们需要此功能,则必须使用类组件。

总结

我们已经看到了 getSnapshotBeforeUpdate() 函数的工作机制。我们还创建了一个小型应用程序来展示该函数的用法。此组件可以包含在我们的 React 应用程序中,以显示 getSnapshotBeforeUpdate 如何在添加新内容时保持滚动位置。

reactjs_reference_api.htm
广告