反应 useEffect 比较对象
react useEffect comparing objects
我正在使用 React useEffect
挂钩并检查对象是否已更改,然后才再次 运行 挂钩。
我的代码是这样的。
const useExample = (apiOptions) => {
const [data, updateData] = useState([]);
useEffect(() => {
const [data, updateData] = useState<any>([]);
doSomethingCool(apiOptions).then(res => {
updateData(response.data);
})
}, [apiOptions]);
return {
data
};
};
不幸的是,它保持 运行ning,因为对象没有被识别为相同。
我相信以下是原因的示例。
const objA = {
method: 'GET'
}
const objB = {
method: 'GET'
}
console.log(objA === objB)
也许 运行宁 JSON.stringify(apiOptions)
有效?
使用apiOptions
作为状态值
我不确定您是如何使用自定义挂钩的,但是通过使用 useState
使 apiOptions
成为状态值应该可以正常工作。这样您就可以将它作为状态值提供给您的自定义挂钩,如下所示:
const [apiOptions, setApiOptions] = useState({ a: 1 })
const { data } = useExample(apiOptions)
这样它只会在您使用 setApiOptions
时改变。
示例 #1
import { useState, useEffect } from 'react';
const useExample = (apiOptions) => {
const [data, updateData] = useState([]);
useEffect(() => {
console.log('effect triggered')
}, [apiOptions]);
return {
data
};
}
export default function App() {
const [apiOptions, setApiOptions] = useState({ a: 1 })
const { data } = useExample(apiOptions);
const [somethingElse, setSomethingElse] = useState('default state')
return <div>
<button onClick={() => { setApiOptions({ a: 1 }) }}>change apiOptions</button>
<button onClick={() => { setSomethingElse('state') }}>
change something else to force rerender
</button>
</div>;
}
或者
您可以按照 here:
的描述写一个深度比较 useEffect
function deepCompareEquals(a, b){
// TODO: implement deep comparison here
// something like lodash
// return _.isEqual(a, b);
}
function useDeepCompareMemoize(value) {
const ref = useRef()
// it can be done by using useMemo as well
// but useRef is rather cleaner and easier
if (!deepCompareEquals(value, ref.current)) {
ref.current = value
}
return ref.current
}
function useDeepCompareEffect(callback, dependencies) {
useEffect(
callback,
dependencies.map(useDeepCompareMemoize)
)
}
您可以像使用 useEffect
一样使用它。
我刚刚找到了适合我的解决方案。
你必须使用 Lodash 的 usePrevious()
和 _.isEqual()
。
在 useEffect()
中,如果 previous apiOptions
等于 current apiOptions
,则放置一个条件。如果为真,什么都不做。如果为假 updateData().
示例:
const useExample = (apiOptions) => {
const myPreviousState = usePrevious(apiOptions);
const [data, updateData] = useState([]);
useEffect(() => {
if (myPreviousState && !_.isEqual(myPreviousState, apiOptions)) {
updateData(apiOptions);
}
}, [apiOptions])
}
usePrevious(value)
是一个自定义挂钩,它使用 useRef()
.
创建 ref
您可以从Official React Hook documentation找到它。
const usePrevious = value => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
};
如果您确定无法控制 apiOptions
,那么只需将原生 useEffect 替换为 https://github.com/kentcdodds/use-deep-compare-effect。
如果输入足够浅,以至于您认为深度相等仍然很快,请考虑使用 JSON.stringify
:
const useExample = (apiOptions) => {
const [data, updateData] = useState([]);
const apiOptionsJsonString = JSON.stringify(apiOptions);
useEffect(() => {
const apiOptionsObject = JSON.parse(apiOptionsJsonString);
doSomethingCool(apiOptionsObject).then(response => {
updateData(response.data);
})
}, [apiOptionsJsonString]);
return {
data
};
};
注意它不会比较函数。
您可以使用 useDeepCompareEffect、useCustomCompareEffect 或编写您自己的钩子。
https://github.com/kentcdodds/use-deep-compare-effect
https://github.com/sanjagh/use-custom-compare-effect
假设您的 apiOptions
的很大一部分是静态的,并且如果组件重新渲染也不会改变,我建议采用以下方法:
const useExample = (apiOptions, deps) => {
useEffect(callback, deps);
return { ... };
};
想法是仅传输效果所依赖的值数组作为第二个参数
在某些情况下真的很简单!
const objA = {
method: 'GET'
}
const objB = {
method: 'GET'
}
console.log(objA === objB) // false
为什么objA不等于objB? Coz JS 只是比较他们的地址,对吗?他们是两个不同的对象。这是我们都知道的!
与 React hooks 一样!
所以,众所周知,objA.method === objB.method 对吗?因为它们是字面意思。
答案出来了:
React.useEffect(() => {
// do your facy work
}, [obj.method])
另一种选择,如果你有能力修改doSomethingCool
:
如果确切知道您需要哪些非 Object
属性,您可以将依赖项列表限制为 useEffect
将使用 ===
正确解释的属性,例如:
const useExample = (apiOptions) => {
const [data, updateData] = useState([]);
useEffect(() => {
const [data, updateData] = useState<any>([]);
doSomethingCool(apiOptions.method).then(res => {
updateData(response.data);
})
}, [apiOptions.method]);
return {
data
};
};
我正在使用 React useEffect
挂钩并检查对象是否已更改,然后才再次 运行 挂钩。
我的代码是这样的。
const useExample = (apiOptions) => {
const [data, updateData] = useState([]);
useEffect(() => {
const [data, updateData] = useState<any>([]);
doSomethingCool(apiOptions).then(res => {
updateData(response.data);
})
}, [apiOptions]);
return {
data
};
};
不幸的是,它保持 运行ning,因为对象没有被识别为相同。
我相信以下是原因的示例。
const objA = {
method: 'GET'
}
const objB = {
method: 'GET'
}
console.log(objA === objB)
也许 运行宁 JSON.stringify(apiOptions)
有效?
使用apiOptions
作为状态值
我不确定您是如何使用自定义挂钩的,但是通过使用 useState
使 apiOptions
成为状态值应该可以正常工作。这样您就可以将它作为状态值提供给您的自定义挂钩,如下所示:
const [apiOptions, setApiOptions] = useState({ a: 1 })
const { data } = useExample(apiOptions)
这样它只会在您使用 setApiOptions
时改变。
示例 #1
import { useState, useEffect } from 'react';
const useExample = (apiOptions) => {
const [data, updateData] = useState([]);
useEffect(() => {
console.log('effect triggered')
}, [apiOptions]);
return {
data
};
}
export default function App() {
const [apiOptions, setApiOptions] = useState({ a: 1 })
const { data } = useExample(apiOptions);
const [somethingElse, setSomethingElse] = useState('default state')
return <div>
<button onClick={() => { setApiOptions({ a: 1 }) }}>change apiOptions</button>
<button onClick={() => { setSomethingElse('state') }}>
change something else to force rerender
</button>
</div>;
}
或者
您可以按照 here:
的描述写一个深度比较useEffect
function deepCompareEquals(a, b){
// TODO: implement deep comparison here
// something like lodash
// return _.isEqual(a, b);
}
function useDeepCompareMemoize(value) {
const ref = useRef()
// it can be done by using useMemo as well
// but useRef is rather cleaner and easier
if (!deepCompareEquals(value, ref.current)) {
ref.current = value
}
return ref.current
}
function useDeepCompareEffect(callback, dependencies) {
useEffect(
callback,
dependencies.map(useDeepCompareMemoize)
)
}
您可以像使用 useEffect
一样使用它。
我刚刚找到了适合我的解决方案。
你必须使用 Lodash 的 usePrevious()
和 _.isEqual()
。
在 useEffect()
中,如果 previous apiOptions
等于 current apiOptions
,则放置一个条件。如果为真,什么都不做。如果为假 updateData().
示例:
const useExample = (apiOptions) => {
const myPreviousState = usePrevious(apiOptions);
const [data, updateData] = useState([]);
useEffect(() => {
if (myPreviousState && !_.isEqual(myPreviousState, apiOptions)) {
updateData(apiOptions);
}
}, [apiOptions])
}
usePrevious(value)
是一个自定义挂钩,它使用 useRef()
.
ref
您可以从Official React Hook documentation找到它。
const usePrevious = value => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
};
如果您确定无法控制 apiOptions
,那么只需将原生 useEffect 替换为 https://github.com/kentcdodds/use-deep-compare-effect。
如果输入足够浅,以至于您认为深度相等仍然很快,请考虑使用 JSON.stringify
:
const useExample = (apiOptions) => {
const [data, updateData] = useState([]);
const apiOptionsJsonString = JSON.stringify(apiOptions);
useEffect(() => {
const apiOptionsObject = JSON.parse(apiOptionsJsonString);
doSomethingCool(apiOptionsObject).then(response => {
updateData(response.data);
})
}, [apiOptionsJsonString]);
return {
data
};
};
注意它不会比较函数。
您可以使用 useDeepCompareEffect、useCustomCompareEffect 或编写您自己的钩子。
https://github.com/kentcdodds/use-deep-compare-effect https://github.com/sanjagh/use-custom-compare-effect
假设您的 apiOptions
的很大一部分是静态的,并且如果组件重新渲染也不会改变,我建议采用以下方法:
const useExample = (apiOptions, deps) => {
useEffect(callback, deps);
return { ... };
};
想法是仅传输效果所依赖的值数组作为第二个参数
在某些情况下真的很简单!
const objA = {
method: 'GET'
}
const objB = {
method: 'GET'
}
console.log(objA === objB) // false
为什么objA不等于objB? Coz JS 只是比较他们的地址,对吗?他们是两个不同的对象。这是我们都知道的!
与 React hooks 一样!
所以,众所周知,objA.method === objB.method 对吗?因为它们是字面意思。
答案出来了:
React.useEffect(() => {
// do your facy work
}, [obj.method])
另一种选择,如果你有能力修改doSomethingCool
:
如果确切知道您需要哪些非 Object
属性,您可以将依赖项列表限制为 useEffect
将使用 ===
正确解释的属性,例如:
const useExample = (apiOptions) => {
const [data, updateData] = useState([]);
useEffect(() => {
const [data, updateData] = useState<any>([]);
doSomethingCool(apiOptions.method).then(res => {
updateData(response.data);
})
}, [apiOptions.method]);
return {
data
};
};