Рассчитать начальное значение в пользовательском хуке - PullRequest
1 голос
/ 11 февраля 2020

В моем приложении реакции у меня есть пользовательский хук useDataObject(), который вычисляет объект на основе значения другого хука, например useScreenWidth(). Моя проблема в том, что я не могу разместить логики вычислений c, поэтому объект сразу готов к первому возврату:


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 завершит sh, пока я не получу действительный dataObject. Это означает, что мне нужно всегда включать нулевую проверку после вызовов useDataObject().

. Каков наилучший подход к выполнению логики вычислений c в 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 голосов
/ 13 февраля 2020

Отвечая на мой собственный вопрос. Я обнаружил, что использование 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]);
}

Полный пример на codesandbox

  • Хук немедленно возвращает вычисленное dataObject.
  • Каждый раз, когда [width] изменяется, вычисление повторяется и возвращается новое значение.
  • Ни один код не дублируется или выполняется слишком часто.

Я не могу гарантировать, что лучшего решения не существует, но для моих целей оно делает именно то, что мне нужно. (Конечно, можно также интегрировать хук useScreenWidth в хук useDataObject. Но в моем проекте мне нужен useScreenWidth отдельно.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...