javascript - 如何编写依赖于反应查询调用的自定义钩子
问题描述
我有一些使用反应查询获取数据并使用该数据进行低级计算的钩子。另一个自定义挂钩将使用该数据的输出来计算不同的值。
例如:
- Hook #1 获取用户租赁财产(例如熔炉、屋顶等)的未来费用清单。
- 钩子#2 获取用户的偏好,即应考虑和保存未来多长时间的费用(例如,仅与未来 5 年内到期的费用相关)。
- Hook #3 调用 hooks #1 和 #2 并返回每个月应该留出的用于支付未来费用的总金额。
由于钩子 #3 依赖于前两个钩子的数据,我不确定如何构造该代码。React 查询返回数据和加载状态,因此我目前正在合并来自钩子 #1 和 #2 的加载状态,并将来自钩子 #3 的数据的计算置于条件中。但这会导致重新渲染循环错误。
知道为什么会这样吗?
const usePropertyTotalMonthlyCapex = (propertyId) => {
const [preferences, preferencesIsLoading, preferencesIsError, preferencesError] = useGetUserPreferences()
const [expenses, isLoading, isError, error] = useGetExpenses(propertyId)
const [capex, setCapex] = useState(0)
if (preferences && expenses) { // this seems to be causing a render loop error
if (expenses.length === 0) {
setCapex(0)
} else {
const { outlookLength } = preferences
const expenseValues = expenses?.map(expense => {
const { lifespan, age, replacementCost } = expense
if (lifespan - age > outlookLength) {
return 0
}
return replacementCost / ((lifespan - age) * 12)
})
setCapex(expenseValues.reduce((acc, init) => acc + init).toFixed(0))
}
}
return [capex, preferencesIsLoading && isLoading]
解决方案
你不需要状态。capex
是可以从您已经拥有的状态计算的派生状态。将派生状态放入状态可能是一种反模式。如果你的 setteruseState
只在一个effect中被调用,你可以发现它。由于计算capex
是纯粹的,您可以在渲染期间调用它:
const computeCapex = (preferences, expenses) => {
if (preferences && expenses) {
if (expenses.length === 0) {
return 0
} else {
const { outlookLength } = preferences
const expenseValues = expenses?.map(expense => {
const { lifespan, age, replacementCost } = expense
if (lifespan - age > outlookLength) {
return 0
}
return replacementCost / ((lifespan - age) * 12)
})
return expenseValues.reduce((acc, init) => acc + init).toFixed(0))
}
}
}
const usePropertyTotalMonthlyCapex = (propertyId) => {
const [preferences, preferencesIsLoading, preferencesIsError, preferencesError] = useGetUserPreferences()
const [expenses, isLoading, isError, error] = useGetExpenses(propertyId)
const capex = computeCapex(preferences, expenses)
return [capex, preferencesIsLoading && isLoading]
}
如果计算成本很高,您可以将调用包装在 中useMemo
,这是为此而设计的:
const usePropertyTotalMonthlyCapex = (propertyId) => {
const [preferences, preferencesIsLoading, preferencesIsError, preferencesError] = useGetUserPreferences()
const [expenses, isLoading, isError, error] = useGetExpenses(propertyId)
const capex = React.useMemo(
() => computeCapex(preferences, expenses),
[preferences, expenses]
)
return [capex, preferencesIsLoading && isLoading]
}
推荐阅读
- visual-studio-code - 如何使用拆分终端启动我的 VS Code 工作区?
- python - 为什么当循环再次开始时我的变量中的计数器被重置
- sql - 如何在 oracle 数据库上管理夏令时(夏令时)?
- android - 约束布局垂直对齐中心 - 两个子视图
- php - 如何在点 php 文件中显示和播放 html5 音频?
- r - R - 从一年中获取夏令时开始和结束
- python - 运行解释器时导入某些模块时出错
- javascript - Javascript 重定向到 URL 控制器
- prolog - 在 SWI Prolog 中用常见的自由变量指示列表
- xcode - 如何在 Xcode 中手动强制索引?