在React中如何对对象深度监听

638 阅读1分钟

JSON.stringfy

首先最容易想到的就是JSON.stringfy把对象转成字符串来比较

import React, { useState, useEffect } from 'react';  

const DeepWatchComponent = () => {  
    const [data, setData] = useState({ name: 'Alice', age: 25 });  

    useEffect(() => {  
        console.log('Data changed:', data);  
    }, [JSON.stringify(data)]); // 深度监听  

    const updateData = () => {  
        setData(prevData => ({ ...prevData, age: prevData.age + 1 }));  
    };  

    return (  
        <div>  
            <h1>{data.name}</h1>  
            <p>Age: {data.age}</p>  
            <button onClick={updateData}>Increase Age</button>  
        </div>  
    );  
};  

export default DeepWatchComponent;

在这个示例中,useEffect会在data对象的任何深层属性变化时触发。每次更新data时,都会将其转换为字符串进行比较,从而实现深度监听。

  • 存在的问题
  1. 如果对象中存在循环引用,JSON.stringify 会抛出错误,导致应用崩溃。
  2. JSON.stringify 会忽略对象中的函数、undefined 和某些特殊对象(如 DateMapSet 等),这可能导致状态丢失或不准确。
  3. 某些值(如 Symbol)无法被序列化,这可能导致数据丢失。

使用useRef和useEffect

使用 useRef 来存储上一个对象的引用,并在 useEffect 中进行手动比较。

import React, { useState, useEffect, useRef } from 'react';  
import isEqual from 'lodash/isEqual'; // 需要安装 lodash  

const DeepWatchComponent = () => {  
    const [data, setData] = useState({ name: 'Alice', age: 25 });  
    const prevDataRef = useRef();  

    useEffect(() => {  
        if (prevDataRef.current && !isEqual(prevDataRef.current, data)) {  
            console.log('Data changed:', data);  
        }  
        prevDataRef.current = data; // 更新上一个数据  
    }, [data]);  

    const updateData = () => {  
        setData(prevData => ({ ...prevData, age: prevData.age + 1 }));  
    };  

    return (  
        <div>  
            <h1>{data.name}</h1>  
            <p>Age: {data.age}</p>  
            <button onClick={updateData}>Increase Age</button>  
        </div>  
    );  
};  

export default DeepWatchComponent;

使用自定义Hook

创建一个自定义 Hook 来处理深度监听。

import React, { useState, useEffect, useRef } from 'react';  
import isEqual from 'lodash/isEqual'; // 需要安装 lodash  

const useDeepCompareEffect = (callback, dependencies) => {  
    const currentDependenciesRef = useRef();  

    if (!isEqual(currentDependenciesRef.current, dependencies)) {  
        currentDependenciesRef.current = dependencies;  
    }  

    useEffect(callback, [currentDependenciesRef.current]);  
};  

const DeepWatchComponent = () => {  
    const [data, setData] = useState({ name: 'Alice', age: 25 });  

    useDeepCompareEffect(() => {  
        console.log('Data changed:', data);  
    }, [data]);  

    const updateData = () => {  
        setData(prevData => ({ ...prevData, age: prevData.age + 1 }));  
    };  

    return (  
        <div>  
            <h1>{data.name}</h1>  
            <p>Age: {data.age}</p>  
            <button onClick={updateData}>Increase Age</button>  
        </div>  
    );  
};  

export default DeepWatchComponent;