Использовать хук внутри функции

Я новичок в реагировании и JS. Я хочу немного провести рефакторинг и очистить свои бесконечные файлы и разделить их на файлы для разных задач. Итак, я создал папку lib и файл с именем CalculPercentage.js, и я хочу использовать там перехватчик useContext. Как этого можно достичь? Нужно ли мне создавать компонент?

это невозможно:

import { addDays, differenceInCalendarDays } from "date-fns";
import { useContext } from "react";
import { TimelineSettingsContext } from "../TimelineSettingsProvider";

// Function to calculate the percentage difference between two dates
export function calculatePercentage(startDate, endDate) {
  const { timelineStart, timelineLength, timelineScale } = useContext(
    TimelineSettingsContext
  );
  //wenn startDate vor dem TimelineStart liegt, dass muss der Balken verkürzt werden und startet bei timelineStart
  startDate =
    differenceInCalendarDays(timelineStart, startDate) < 0
      ? startDate
      : timelineStart;
  const startPosition =
    (differenceInCalendarDays(startDate, timelineStart) / timelineLength) *
    timelineScale;
  const width =
    (differenceInCalendarDays(addDays(endDate, 1), startDate) /
      timelineLength) *
    timelineScale;
  return { startPosition, width };
}

Теперь, после решения этой ошибки с комментарием ниже. Получение еще одной ошибки от вызывающего компонента:

import { differenceInCalendarDays } from "date-fns";
import React, { useContext } from "react";
import BackgroundColumns from "./BackgroundColumns";
import BookingBar from "./BookingBar";
import { TimelineSettingsContext } from "../../TimelineSettingsProvider";
import { useCalculatePercentage } from "../../lib/calculatePercentage";

const Timeline = () => {
  const { groupedBookings, dates, setSelectedBooking } = useContext(
    TimelineSettingsContext
  );

  function handleBookingSelected(booking) {
    setSelectedBooking(booking.LfdNr);
  }

  return (
    <div className = "whitespace-nowrap relative">
      <div className = "absolute flex w-full h-full">
        {dates?.map((date, index) => {
          const { width } = useCalculatePercentage(new Date(), new Date());
          return (
            <BackgroundColumns
              key = {index}
              date = {date}
              width = {width}
            ></BackgroundColumns>
          );
        })}
      </div>
      <div className = "relative flex flex-col z-20 pt-8">
        {groupedBookings.map((category, index) => {
          return (
            <React.Fragment key = {index}>
              <div
                className = "h-8"
                key = {`${index}`} // Verwenden Sie eine eindeutige Kombination aus index und idx für den key
              />
              {category.bookings.map((booking, idx) => {
                const { startPosition, width } = useCalculatePercentage(
                  new Date(booking.Beginn_Datum),
                  new Date(booking.Ende_Datum)
                );
                const bookingLength =
                  differenceInCalendarDays(
                    booking.Ende_Datum,
                    booking.Beginn_Datum
                  ) + 1;

                return (
                  <BookingBar
                    callbackBarClicked = {() => handleBookingSelected(booking)}
                    key = {booking.LfdNr} // Verwenden Sie eine eindeutige Kombination aus index und idx für den key
                    startPosition = {startPosition}
                    width = {width}
                    index = {index}
                    label = {`${bookingLength} ${
                      bookingLength > 1 ? "days" : "day"
                    }`}
                  />
                );
              })}
            </React.Fragment>
          );
        })}
      </div>
    </div>
  );
};

export default Timeline;

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Да, как вы заметили, в функциях нельзя использовать хуки. Перехватчики React можно вызывать только внутри функционального компонента или из другого пользовательского перехватчика. Поэтому, имея дело с возвратами, отличными от JSX, вы должны создать еще один крючок, из которого вы сможете вызывать нужные вам хуки. Итак, в этом случае вы, скорее всего, напишете что-то вроде

export function useCalculatePercentage(startDate, endDate) {
  const { timelineStart, timelineLength, timelineScale } = useContext(
    TimelineSettingsContext,
  );
  // ...
  return { startPosition, width };
}

И тогда у вас есть новый крючок, который вы можете использовать для получения нужного вам процентного значения. И это снова необходимо вызвать на верхнем уровне в функциональном компоненте или другом хуке.

В вашем случае, когда вы используете useCalculatePercentage, он не будет работать должным образом или будет выдавать предупреждения. Вы должны вызывать перехватчик на верхнем уровне функции, чтобы не было циклов и обратных вызовов, просто где-то здесь.

const Timeline = () => {
  const { groupedBookings, dates, setSelectedBooking } = useContext(
    TimelineSettingsContext
  );
  //<--- call hook


  function handleBookingSelected(booking) {
    setSelectedBooking(booking.LfdNr);
  }

  //<--- or  here

Я думаю, что это как-то связано с тем, как реакция отслеживает хуки, поэтому, по сути, одна из главных вещей заключается в том, что количество вызовов хука должно быть одинаковым для всех рендереров и даже иногда вызывать хуки на картах и ​​т. д., как в вашем примере. может выдавать постоянное количество вызовов перехвата в течение необходимого периода времени, но это не гарантия, поэтому реакция предупреждает об этом. Так что это как бы противоречит дизайну реагирования, поэтому не стоит головной боли.

В вашей ситуации для первого вызова легко переместить вызов в перехватчик за пределами любых обратных вызовов, поскольку аргументами являются только текущая дата, поэтому она одинакова для всех элементов, которые будут повторяться в функции карты. Но второй обратный вызов более сложен. Но если вы хотите использовать здесь хук, вы можете вернуть функцию из хука. Таким образом, по сути, сам хук может получить все переменные, необходимые для реакции, поэтому другие хуки, включая значения контекста, как в вашем случае, возвращают функцию, которая принимает обычные переменные, а затем возвращает значение. Так может выглядеть что-то вроде

export function useCalculatePercentageWithContext() {
  const { timelineStart, timelineLength, timelineScale } = useContext(
    TimelineSettingsContext,
  );

  const calculatePercentage = (start, end) => {
    //...
    return { startPosition, width };
  };
  return calculatePercentage;
}

и поскольку вычисление процента — это обычная функция, вы можете вызывать ее где угодно, поэтому используйте ее как

const Timeline = () => {
  const { groupedBookings, dates, setSelectedBooking } = useContext(
    TimelineSettingsContext
  );
  const calculatePercentage = useCalculatePercentageWithContext();

  //....

{category.bookings.map((booking, idx) => {
     const { startPosition, width } = calculatePercentage(
        new Date(booking.Beginn_Datum),new Date(booking.Ende_Datum))...}

И это будет работать нормально, но, на мой взгляд, наличие всех этих хуков может быстро надоесть, и каждый раз, когда вы хотите добавить какую-то функциональность, вам придется создавать еще один крючок, использующий несколько хуков и т. д.

Если бы я проводил рефакторинг, я бы вызвал контекстный хук в компоненте, имел бы где-нибудь нормальную функцию и просто передал бы ей аргументы, в том числе из контекста. Что-то вроде

export function calculatePercentage({
  timelineStart,
  timelineLength,
  timelineScale,
  startDate,
  endDate,
}) {
  ///... logic
  return { startPosition, width };
}

затем в временной шкале

const { timelineStart, timelineLength, timelineScale } = useContext(
  TimelineSettingsContext,
);

и

{category.bookings.map((booking, idx) => {
  const { startPosition, width } = calculatePercentage({
     start:new Date(booking.Beginn_Datum),
     end:new Date(booking.Ende_Datum),
     timelineStart,
     timelineLength,
     timelineScale,
  });

и я думаю, что так будет намного проще.

эта ошибка исчезла, но теперь появляется: Ошибка: React Hook "useCalculatePercentage" не может быть вызван внутри обратного вызова. React Hooks необходимо вызывать в функциональном компоненте React или в пользовательской функции React Hook. Я добавлю вызывающий компонент в свой пост

beanCounter 07.07.2024 17:28

в реакции важно, чтобы вы не вызывали перехваты за пределами тела верхнего уровня функционального компонента или даже перехвата. Так что, если вы вызовете его на карте, это не сработает. Теоретически, я думаю, это возможно, если длина массива фиксирована в течение определенного жизненного цикла, но они не предназначены для такого использования. Если вы просто посмотрите правила реагирования перехватчиков, вы увидите хорошее объяснение. Но если вы добавите остальную часть кода, где вы вызываете хук, вы сможете это изучить.

Max 07.07.2024 17:42

о, это кажется интересным, но с моими нынешними знаниями, боюсь, я не пойму этого, если посмотрю код через несколько недель. Поэтому я думаю, что самое простое решение — передать недостающие свойства в функцию вместо использования перехватчика useContext.

beanCounter 07.07.2024 18:01

ахах да, это интересно, но и неприятно, я бы честно предложил использовать специальные хуки только в тех ситуациях, когда это чрезвычайно полезно. В ответ я добавил, что считаю, что гораздо проще и, на мой взгляд, лучшее решение — просто получить контекстные переменные в компоненте и передать их функции.

Max 07.07.2024 18:05

Я добавлю пример

Max 07.07.2024 18:06

хаха, ладно, вот откуда я пришел. Но имеет смысл. Я вернусь к этому.

beanCounter 07.07.2024 19:32

Другие вопросы по теме