计算自定义挂钩中的初始值

时间:2020-02-11 17:04:53

标签: reactjs react-hooks

在我的react应用中,我有一个自定义钩子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>
        </>
    );
}

查看此Sandbox

由于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;
}

但这看起来很笨拙而且令人困惑。这也给我一个错误的错误,告诉我应该使用useCallback()。有没有更直接的方法? 谢谢!

1 个答案:

答案 0 :(得分:0)

回答我自己的问题。我发现使用useMemo()钩子(请参阅docs)代替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]);
}

Full example on codesandbox

  • 该钩子立即返回计算出的dataObject
  • 每次[width]更改时,都会重做计算并返回新值。
  • 没有重复或频繁执行的代码。

我不能保证没有更好的解决方案,但就我的目的而言,它确实可以满足我的需求。 (当然,也可以将useScreenWidth挂钩集成到useDataObject挂钩中。但是在我的项目中,我需要单独使用useScreenWidth。)