欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React PureComponent中引用類型修改導(dǎo)致頁面不更新的解決方案

 更新時間:2025年09月15日 09:25:00   作者:北辰alk  
React的PureComponent是React.Component的一個變體,它通過淺比較props和state來自動實現(xiàn)shouldComponentUpdate()方法,從而優(yōu)化性能,本文詳細解析React PureComponent中引用類型數(shù)據(jù)修改導(dǎo)致頁面不更新的問題,并提供多種解決方案和最佳實踐,需要的朋友可以參考下

PureComponent 的工作原理

什么是 PureComponent

PureComponent 是 React 提供的一個優(yōu)化性能的組件基類,它通過淺比較(shallow comparison)來自動實現(xiàn) shouldComponentUpdate 方法。

import React, { PureComponent } from 'react';

class MyComponent extends PureComponent {
  render() {
    return <div>{this.props.value}</div>;
  }
}

淺比較機制

PureComponent 的淺比較機制如下:

// 簡化的淺比較實現(xiàn)
function shallowEqual(objA, objB) {
  if (objA === objB) {
    return true;
  }

  if (typeof objA !== 'object' || objA === null ||
      typeof objB !== 'object' || objB === null) {
    return false;
  }

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  for (let i = 0; i < keysA.length; i++) {
    const key = keysA[i];
    // 只比較第一層屬性
    if (!objB.hasOwnProperty(key) || objA[key] !== objB[key]) {
      return false;
    }
  }

  return true;
}

PureComponent 與 Component 的區(qū)別

特性ComponentPureComponent
是否需要手動實現(xiàn) shouldComponentUpdate
性能優(yōu)化需要手動優(yōu)化自動淺比較優(yōu)化
適用場景需要精細控制更新的組件數(shù)據(jù)結(jié)構(gòu)簡單的展示組件

問題根源分析

引用類型的特點

JavaScript 中的引用類型(對象、數(shù)組)在賦值時傳遞的是引用(內(nèi)存地址),而不是實際的值。

const obj1 = { count: 0 };
const obj2 = obj1; // obj2 和 obj1 指向同一個內(nèi)存地址

obj2.count = 1;
console.log(obj1.count); // 輸出 1,因為修改的是同一個對象

問題場景再現(xiàn)

class UserList extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      users: [
        { id: 1, name: 'Alice' },
        { id: 2, name: 'Bob' }
      ]
    };
  }

  // 錯誤的方式:直接修改引用類型
  updateUserName = (userId, newName) => {
    const users = this.state.users;
    const user = users.find(u => u.id === userId);
    user.name = newName; // 直接修改原對象
    
    this.setState({ users }); // users 引用未改變,PureComponent 不會重新渲染
  }

  render() {
    return (
      <div>
        {this.state.users.map(user => (
          <UserItem 
            key={user.id} 
            user={user} 
            onUpdate={this.updateUserName}
          />
        ))}
      </div>
    );
  }
}

問題流程圖解

解決方案概覽

解決方案對比表

解決方案優(yōu)點缺點適用場景
不可變數(shù)據(jù)模式性能好,易于調(diào)試需要學(xué)習(xí)新概念大多數(shù)場景
狀態(tài)管理庫功能強大,生態(tài)豐富增加項目復(fù)雜度大型應(yīng)用
forceUpdate簡單直接違背 React 設(shè)計原則緊急修復(fù)
函數(shù)式子組件靈活可控需要手動優(yōu)化性能簡單組件

方案一:使用不可變數(shù)據(jù)模式

什么是不可變數(shù)據(jù)

不可變數(shù)據(jù)是指一旦創(chuàng)建就不能被修改的數(shù)據(jù)。任何修改都會返回一個新的數(shù)據(jù)副本。

使用擴展運算符

updateUserName = (userId, newName) => {
  this.setState(prevState => ({
    users: prevState.users.map(user => 
      user.id === userId 
        ? { ...user, name: newName } // 創(chuàng)建新對象
        : user
    )
  }));
}

使用數(shù)組的不可變方法

// 添加用戶
addUser = (newUser) => {
  this.setState(prevState => ({
    users: [...prevState.users, newUser] // 創(chuàng)建新數(shù)組
  }));
}

// 刪除用戶
removeUser = (userId) => {
  this.setState(prevState => ({
    users: prevState.users.filter(user => user.id !== userId) // 創(chuàng)建新數(shù)組
  }));
}

// 更新用戶
updateUser = (userId, updates) => {
  this.setState(prevState => ({
    users: prevState.users.map(user => 
      user.id === userId 
        ? { ...user, ...updates } // 創(chuàng)建新對象
        : user
    )
  }));
}

使用 Object.assign

updateUserName = (userId, newName) => {
  this.setState(prevState => ({
    users: prevState.users.map(user => 
      user.id === userId 
        ? Object.assign({}, user, { name: newName }) // 創(chuàng)建新對象
        : user
    )
  }));
}

處理嵌套對象

對于深層嵌套的對象,需要使用遞歸或?qū)S脦欤?/p>

// 深層更新示例
updateUserProfile = (userId, field, value) => {
  this.setState(prevState => ({
    users: prevState.users.map(user => 
      user.id === userId 
        ? { 
            ...user, 
            profile: {
              ...user.profile,
              [field]: value
            }
          }
        : user
    )
  }));
}

方案二:使用狀態(tài)管理庫

使用 Immer 簡化不可變更新

Immer 讓你可以用可變的方式編寫不可變更新邏輯。

npm install immer
import produce from 'immer';

class UserList extends PureComponent {
  // 使用 Immer 進行更新
  updateUserName = (userId, newName) => {
    this.setState(produce(draft => {
      const user = draft.users.find(u => u.id === userId);
      if (user) {
        user.name = newName; // 直接修改,Immer 會處理不可變性
      }
    }));
  }
}

使用 Redux 進行狀態(tài)管理

// actions.js
export const updateUserName = (userId, name) => ({
  type: 'UPDATE_USER_NAME',
  payload: { userId, name }
});

// reducer.js
const initialState = {
  users: [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
  ]
};

export function userReducer(state = initialState, action) {
  switch (action.type) {
    case 'UPDATE_USER_NAME':
      return {
        ...state,
        users: state.users.map(user =>
          user.id === action.payload.userId
            ? { ...user, name: action.payload.name }
            : user
        )
      };
    default:
      return state;
  }
}

使用 MobX 進行狀態(tài)管理

npm install mobx mobx-react
import { observable, action } from 'mobx';
import { observer } from 'mobx-react';

class UserStore {
  @observable users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
  ];

  @action
  updateUserName = (userId, newName) => {
    const user = this.users.find(u => u.id === userId);
    if (user) {
      user.name = newName; // MobX 會檢測變化并觸發(fā)更新
    }
  }
}

const userStore = new UserStore();

// 使用 observer 包裝組件
const UserList = observer(({ store }) => (
  <div>
    {store.users.map(user => (
      <div key={user.id}>{user.name}</div>
    ))}
  </div>
));

方案三:使用 forceUpdate 方法

什么是 forceUpdate

forceUpdate() 是 React 組件的一個方法,它會強制組件重新渲染,跳過 shouldComponentUpdate

使用示例

class UserList extends PureComponent {
  updateUserName = (userId, newName) => {
    const users = this.state.users;
    const user = users.find(u => u.id === userId);
    user.name = newName; // 直接修改原對象
    
    this.forceUpdate(); // 強制重新渲染
  }
}

注意事項

  1. 不推薦常規(guī)使用forceUpdate 違背了 React 的數(shù)據(jù)流原則
  2. 性能影響:跳過 shouldComponentUpdate 可能導(dǎo)致不必要的渲染
  3. 使用場景:僅適用于無法通過正常數(shù)據(jù)流更新的特殊情況

替代方案:使用 key 屬性

通過改變 key 值強制重新創(chuàng)建組件:

class UserList extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      users: [...],
      version: 0 // 用作 key 的值
    };
  }

  updateUserName = (userId, newName) => {
    const users = this.state.users;
    const user = users.find(u => u.id === userId);
    user.name = newName;
    
    // 通過改變 version 強制重新渲染
    this.setState(prevState => ({ version: prevState.version + 1 }));
  }

  render() {
    return (
      <div key={this.state.version}>
        {this.state.users.map(user => (
          <UserItem key={user.id} user={user} />
        ))}
      </div>
    );
  }
}

方案四:使用函數(shù)式子組件

將可變部分提取為獨立組件

// UserItem.js - 使用普通 Component
class UserItem extends Component {
  shouldComponentUpdate(nextProps) {
    // 自定義比較邏輯
    return nextProps.user.name !== this.props.user.name;
  }

  render() {
    const { user } = this.props;
    return <div>{user.name}</div>;
  }
}

// UserList.js - 繼續(xù)使用 PureComponent
class UserList extends PureComponent {
  // 仍然使用直接修改(不推薦,僅作示例)
  updateUserName = (userId, newName) => {
    const users = this.state.users;
    const user = users.find(u => u.id === userId);
    user.name = newName;
    
    this.setState({ users });
  }

  render() {
    return (
      <div>
        {this.state.users.map(user => (
          <UserItem 
            key={user.id} 
            user={user} 
            onUpdate={this.updateUserName}
          />
        ))}
      </div>
    );
  }
}

使用 React.memo 自定義比較

const UserItem = React.memo(({ user }) => {
  return <div>{user.name}</div>;
}, (prevProps, nextProps) => {
  // 自定義比較函數(shù)
  return prevProps.user.name === nextProps.user.name;
});

// 在父組件中
class UserList extends PureComponent {
  // 更新邏輯...
}

性能優(yōu)化建議

使用不可變數(shù)據(jù)結(jié)構(gòu)的性能考慮

  1. 結(jié)構(gòu)共享:高級不可變庫(如 Immutable.js)使用結(jié)構(gòu)共享來減少內(nèi)存使用
  2. 避免深層克隆:使用不可變更新時避免不必要的深層克隆
// 不好的做法:深層克隆整個對象
const newState = JSON.parse(JSON.stringify(prevState));

// 好的做法:淺層擴展
const newState = { ...prevState, users: updatedUsers };

使用 reselect 優(yōu)化選擇器

npm install reselect
import { createSelector } from 'reselect';

// 輸入選擇器
const getUsers = state => state.users;
const getFilter = state => state.filter;

// 記憶化選擇器
export const getVisibleUsers = createSelector(
  [getUsers, getFilter],
  (users, filter) => {
    return users.filter(user => 
      user.name.toLowerCase().includes(filter.toLowerCase())
    );
  }
);

// 在組件中使用
const mapStateToProps = state => ({
  visibleUsers: getVisibleUsers(state)
});

使用 React Profiler 分析性能

import { Profiler } from 'react';

function onRenderCallback(
  id, // 發(fā)生提交的 Profiler 樹的 "id"
  phase, // "mount" (如果組件樹剛加載) 或者 "update" (如果它重渲染了)之一
  actualDuration, // 本次更新 committed 花費的渲染時間
  baseDuration, // 估計不使用 memoization 的情況下渲染整顆子樹需要的時間
  startTime, // 本次更新中 React 開始渲染的時間
  commitTime, // 本次更新中 React committed 的時間
  interactions // 屬于本次更新的 interactions 的集合
) {
  // 合計或記錄渲染時間...
}

<Profiler id="UserList" onRender={onRenderCallback}>
  <UserList {...props} />
</Profiler>

總結(jié)與最佳實踐

問題解決總結(jié)

  1. 根本原因:PureComponent 的淺比較無法檢測引用類型內(nèi)部的變化
  2. 核心解決方案:使用不可變數(shù)據(jù)模式創(chuàng)建新對象/數(shù)組而不是修改原對象
  3. 輔助方案:狀態(tài)管理庫、forceUpdate、組件結(jié)構(gòu)優(yōu)化

最佳實踐推薦

  1. 優(yōu)先使用不可變數(shù)據(jù)模式:使用擴展運算符、map、filter 等方法
  2. 復(fù)雜場景使用 Immer:簡化深層不可變更新的編寫
  3. 大型應(yīng)用使用狀態(tài)管理庫:Redux + 不可變更新或 MobX
  4. 避免使用 forceUpdate:除非在極其特殊的情況下
  5. 合理使用 PureComponent:在數(shù)據(jù)結(jié)構(gòu)簡單、渲染成本高的組件中使用

代碼示例:完整解決方案

import React, { PureComponent } from 'react';

class UserManager extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      users: [
        { id: 1, name: 'Alice', profile: { age: 25, city: 'Beijing' } },
        { id: 2, name: 'Bob', profile: { age: 30, city: 'Shanghai' } }
      ]
    };
  }

  // 添加用戶 - 使用不可變更新
  addUser = (newUser) => {
    this.setState(prevState => ({
      users: [...prevState.users, { ...newUser, id: Date.now() }]
    }));
  }

  // 更新用戶信息 - 使用不可變更新
  updateUser = (userId, updates) => {
    this.setState(prevState => ({
      users: prevState.users.map(user =>
        user.id === userId
          ? { ...user, ...updates }
          : user
      )
    }));
  }

  // 更新用戶配置 - 深層不可變更新
  updateUserProfile = (userId, profileUpdates) => {
    this.setState(prevState => ({
      users: prevState.users.map(user =>
        user.id === userId
          ? {
              ...user,
              profile: {
                ...user.profile,
                ...profileUpdates
              }
            }
          : user
      )
    }));
  }

  // 刪除用戶 - 使用不可變更新
  removeUser = (userId) => {
    this.setState(prevState => ({
      users: prevState.users.filter(user => user.id !== userId)
    }));
  }

  render() {
    return (
      <div>
        <UserForm onSubmit={this.addUser} />
        <UserList 
          users={this.state.users}
          onUpdate={this.updateUser}
          onUpdateProfile={this.updateUserProfile}
          onRemove={this.removeUser}
        />
      </div>
    );
  }
}

// 使用 React.memo 優(yōu)化子組件
const UserList = React.memo(({ users, onUpdate, onUpdateProfile, onRemove }) => {
  return (
    <div>
      {users.map(user => (
        <UserItem
          key={user.id}
          user={user}
          onUpdate={onUpdate}
          onUpdateProfile={onUpdateProfile}
          onRemove={onRemove}
        />
      ))}
    </div>
  );
});

export default UserManager;

以上就是React PureComponent中引用類型修改導(dǎo)致頁面不更新的解決方案的詳細內(nèi)容,更多關(guān)于React PureComponent類型修改頁面不更新的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React+Antd 實現(xiàn)可增刪改表格的示例

    React+Antd 實現(xiàn)可增刪改表格的示例

    這篇文章主要介紹了React+Antd實現(xiàn)可增刪改表格的示例,幫助大家更好的理解和學(xué)習(xí)使用React,感興趣的朋友可以了解下
    2021-04-04
  • React從react-router路由上做登陸驗證控制的方法

    React從react-router路由上做登陸驗證控制的方法

    本篇文章主要介紹了React從react-router路由上做登陸驗證控制的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • React中Provider組件詳解(使用場景)

    React中Provider組件詳解(使用場景)

    這篇文章主要介紹了React中Provider組件使用場景,使用Provider可以解決數(shù)據(jù)層層傳遞和每個組件都要傳props的問題,本文結(jié)合示例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2024-02-02
  • create-react-app構(gòu)建項目慢的解決方法

    create-react-app構(gòu)建項目慢的解決方法

    這篇文章主要介紹了create-react-app構(gòu)建項目慢的解決方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • React 實現(xiàn)車牌鍵盤的示例代碼

    React 實現(xiàn)車牌鍵盤的示例代碼

    這篇文章主要介紹了React 實現(xiàn)車牌鍵盤的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • React?Hooks之usePolymerAction抽象代碼結(jié)構(gòu)設(shè)計理念

    React?Hooks之usePolymerAction抽象代碼結(jié)構(gòu)設(shè)計理念

    這篇文章主要為大家介紹了React?Hooks之usePolymerAction抽象代碼結(jié)構(gòu)設(shè)計理念,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • react-router實現(xiàn)跳轉(zhuǎn)傳值的方法示例

    react-router實現(xiàn)跳轉(zhuǎn)傳值的方法示例

    這篇文章主要給大家介紹了關(guān)于react-router實現(xiàn)跳轉(zhuǎn)傳值的相關(guān)資料,文中給出了詳細的示例代碼,對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。
    2017-05-05
  • React受控組件與非受控組件實例分析講解

    React受控組件與非受控組件實例分析講解

    具體來說這是一種react非受控組件,其狀態(tài)是在input的react內(nèi)部控制,不受調(diào)用者控制??梢允褂檬芸亟M件來實現(xiàn)。下面就說說這個React中的受控組件與非受控組件的相關(guān)知識,感興趣的朋友一起看看吧
    2023-01-01
  • React使用useEffect解決setState副作用詳解

    React使用useEffect解決setState副作用詳解

    這篇文章主要為大家介紹了React使用useEffect解決setState副作用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • react實現(xiàn)每隔60s刷新一次接口的示例代碼

    react實現(xiàn)每隔60s刷新一次接口的示例代碼

    本文主要介紹了react實現(xiàn)每隔60s刷新一次接口的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06

最新評論