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时,都会将其转换为字符串进行比较,从而实现深度监听。
- 存在的问题
- 如果对象中存在循环引用,
JSON.stringify会抛出错误,导致应用崩溃。 JSON.stringify会忽略对象中的函数、undefined和某些特殊对象(如Date、Map、Set等),这可能导致状态丢失或不准确。- 某些值(如
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;