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
廣告
© . All rights reserved.