首页 > 解决方案 > 如何编写依赖于反应查询调用的自定义钩子

问题描述

我有一些使用反应查询获取数据并使用该数据进行低级计算的钩子。另一个自定义挂钩将使用该数据的输出来计算不同的值。

例如:

由于钩子 #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]

标签: javascriptreactjsnext.jsreact-query

解决方案


你不需要状态。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]
}


推荐阅读