在我的反应应用程序中,我有一个自定义钩子useDataObject()
,它根据另一个钩子的值计算一个对象,例如useScreenWidth()
.我的问题是,我不知道在哪里放置计算逻辑,所以对象在第一次返回时立即准备就绪:
function useScreenWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return width;
}
function useDataObject() {
const width = useScreenWidth();
const [dataObject, setDataObject] = useState();
useEffect(() => {
// Some logic that creates dataObject from width
const dataObject = {
firstValue: 2 * width,
secondValue: width + 10
};
setDataObject(dataObject);
}, [width]);
return dataObject;
}
export default function App() {
const dataObject = useDataObject();
// Can I get rid of this check?
// Currently needed to prevent "Can not read property firstValue of undefined
if (!dataObject) {
return null;
}
return (
<>
<div>Data object first value: {dataObject.firstValue}</div>
<div>Data object second value: {dataObject.secondValue}</div>
</>
);
}
查看此沙盒
因为useDataObject
没有初始状态,我需要等待第一个useEffect
完成,直到我得到一个有效的dataObject
。这意味着我需要在调用useDataObject()
后始终包含一个空检查。
在第一次返回之前执行计算逻辑的最佳方法是什么,然后在每次宽度更改时执行useDataObject()
?
我尝试的一种方法是创建一个执行计算的内部函数:
function useDataObject() {
const width = useScreenWidth();
const [dataObject, setDataObject] = useState(calculateDataObject());
function calculateDataObject() {
// Some logic that creates dataObject from width
const dataObject = {
firstValue: 2 * width,
secondValue: width + 10
};
return dataObject;
}
useEffect(() => {
setDataObject(calculateDataObject());
}, [width, calculateDataObject]);
return dataObject;
}
但这看起来非常笨拙和混乱。它还给了我一个 linting 错误,告诉我应该使用useCallback()
.难道没有更直接的方法吗? 谢谢!
回答我自己的问题。我发现使用useMemo()
钩子(请参阅文档(而不是useState()
+useEffect()
是我的方案的可能解决方案:
function useDataObject() {
const width = useScreenWidth();
return useMemo(() => {
// Do some calculation with width
const dataObject = {
firstValue: 2 * width,
secondValue: width + 10
};
return dataObject;
}, [width]);
}
代码沙箱的完整示例
- 挂钩会立即返回计算出的
dataObject
。 - 每次更改
[width]
时,都会重做计算并返回新值。 - 没有代码重复或执行得太频繁。
我不能保证没有更好的解决方案,但就我的目的而言,它完全符合我的需求。 (当然,也可以将useScreenWidth
钩集成到useDataObject
钩中。但是在我的项目中,我需要单独useScreenWidth
。