Intersection Observer не заслуживает доверия?

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

Весь код доступен в песочнице кода с демонстрацией, воспроизводящей ошибку:

 Изменить dawn-sunset-9ki0d

Я использую хук useInView для наблюдателя пересечения:

export const useInView = options => {
  const ref = useRef();
  const [isVisible, setIsVisible] = useState(false);
  const [intersectionRatio, setIntersectionRatio] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      console.log("called");
      setIsVisible(entry.isIntersecting);
      setIntersectionRatio(entry.intersectionRatio);
    }, options);

    if (ref.current) observer.observe(ref.current);

    return () => {
      if (ref.current) observer.unobserve(ref.current);
    };
  }, [ref, options]);

  return [ref, isVisible, intersectionRatio];
};

Вот таблица, в которой я визуализирую данные:



 <div id="my-table" style={{ height: 200, width: 200, overflow: "auto" }}>
    <table>
      <tbody>
        {tableValues.map((row, rowIndex) => (
          <tr key={rowIndex}>
            {row.map((cell, cellIndex) => (
              <CellRendererContainer
                key={`${rowIndex}${cellIndex}`}
                rowIndex={rowIndex}
                cellIndex={cellIndex}
                tableCell={cell}
              />
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  </div>

Наблюдатель пересечения используется в CellRenderer, который разделен на два файла:

CellRendererContainer.js

const CellRendererContainer = ({ rowIndex, cellIndex, tableCell }) => {
  const [ref, isVisible, intersectionRatio] = useInView({
    root: document.querySelector("#my-table"),
    rootMargin: "0px",
    threshold: 0.0
  });

  return (
    <CellRenderer
      ref={ref}
      isVisible={isVisible}
      intersectionRatio={intersectionRatio}
      rowIndex={rowIndex}
      cellIndex={cellIndex}
      tableCell={tableCell}
    />
  );
};

А вот фактический рендеринг ячейки CellRenderer.js

const CellRenderer = React.forwardRef(
  ({ isVisible, intersectionRatio, rowIndex, cellIndex, tableCell }, ref) => (
    <td
      ref={ref}
      style={{
        padding: 25,
        backgroundColor:
          rowIndex > 0 && cellIndex > 0 && isVisible && intersectionRatio > 0.9
            ? "red"
            : "white"
      }}
    >
      {tableCell}
    </td>
  )
);

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

Ожидаемый результат:

Любая ячейка, которая становится видимой, должна иметь красный фон, как только она становится видимой полностью. В противном случае эта ячейка должна быть белой.

Ссылка на песочницу кода: (чтобы не нужно было прокручивать вверх)

 Изменить dawn-sunset-9ki0d


person Vishal    schedule 26.11.2019    source источник
comment
Я достаточно осмотрелся, чтобы увидеть, что события запускаются правильно, но ваш код, похоже, не всегда правильно обновляет цвет в ответ на событие. (Разумное использование console.log может помочь в отладке.)   -  person Ouroborus    schedule 26.11.2019
comment
@Ouroborus Спасибо за проверку. Я буду отлаживать рендеринг и цветокоррекцию.   -  person Vishal    schedule 26.11.2019


Ответы (1)


Подпись IntersectionObserver constructor:

var observer = new IntersectionObserver(callback[, options]);

Аргумент options является необязательным и, если он указан, должен быть объектом со свойствами, описывающими поведение вновь созданного IntersectionObserver.

В hook.js у вас есть такая строка:

const observer = new IntersectionObserver(([entry]) => {
  console.log("called");
  setIsVisible(entry.isIntersecting);
  setIntersectionRatio(entry.intersectionRatio);
}, options);

Для вашей переменной options не установлено значение, которое было бы полезно в этом контексте.

Вместо этого что-то вроде этого делает то, что вы ищете:

const observer = new IntersectionObserver(([entry]) => {
  console.log("called");
  setIsVisible(entry.isIntersecting);
  setIntersectionRatio(entry.intersectionRatio);
}, {
  threshold: 0.9
});

После этого изменения событие будет инициироваться, когда соответствующие элементы станут более или менее видимыми более чем на 90%.

person Ouroborus    schedule 30.11.2019
comment
Если я проверю это с более высоким порогом, например 1.0 или 0.9, то, похоже, он работает нормально. Но если я отправлю 0.0 на порог, то это не сработает. Вы можете объяснить почему? - person Vishal; 30.11.2019
comment
@Vishal correctionRatio используется для определения перехода между скрытым и видимым. Если вы установите его на 0, вы никогда не получите переход, потому что элемент никогда не становится скрытым (никогда не имеет отношения пересечения меньше 0). - person Ouroborus; 30.11.2019
comment
Большое спасибо за то, что развеяли мои сомнения. У меня есть еще 2 сомнения, если вы сможете их объяснить, это будет больше, чем помочь мне выйти из этого сценария. Q1: Я знаю, что порог также может принимать массив чисел от 0,0 до 1,0. Какая польза от этого массива. Связан ли этот массив с моим сценарием? Q2: Если я не пропущу root и rootMargin, примет ли он значения по умолчанию? Если да, то если я перейду только в root, то будет ли по умолчанию установлено значение threshhold = 1.0? - person Vishal; 30.11.2019
comment
Codeandbox - это просто пример. Мне нужно проверить это в моем реальном сценарии. Я надеюсь, что это сработает. Мой настоящий код проекта находится на моем офисном ПК. Итак, я проверю это в понедельник, а затем вернусь к вам. Поскольку сейчас мой вопрос решен, я отмечу этот ответ как принятый. - person Vishal; 30.11.2019
comment
Согласно правилам stackoverflow, мне будет разрешено назначить награду только через 24 часа. - person Vishal; 30.11.2019
comment
Существует пример использования массива пороговых значений . Как и следовало ожидать, вы получите событие для каждого перехода, установленного в списке пороговых значений. Например, [0.4, 0.6] сообщит вам о событиях, когда будет нарушена видимость 40% или 60%. Таким образом, вы можете получить несколько событий по мере увеличения или уменьшения видимости элемента. - person Ouroborus; 01.12.2019
comment
Если вы не укажете конкретное свойство в объекте параметров, оно примет значение по умолчанию. - person Ouroborus; 01.12.2019