Лентите за напредъка на четенето, като този, който можете да намерите в моя блог в горната част на единични публикации, са хубаво малко допълнение за предоставяне на подробна информация за това докъде е напреднал читателят в текущата публикация. Лентата за превъртане не е многозначителна в това отношение; включва цялата ви страница, което означава, че заглавката, коментарите, долният колонтитул и т.н. са част от индикацията.

Създаването на лента за прогрес на четене, която ви казва действителния напредък само на текущото съдържание на публикация в React, е доста лесно — особено с кукички, които правят нашия компонент още по-малък.

Компонентът ReadingProgress

Нашият компонент ReadingProgress ще направи следните неща:

  • използвайте куката, която ще отговаря за четенето и настройването на напредъка ни при четене
  • използвайте куката, която ще отговаря за обработката на събитието за превъртане и правилно актуализирайте нашата лента за напредък при превъртане
  • върнете лентата за прогрес на четене с правилната ширина

Така че нека се потопим направо в изпълнението:

const ReadingProgress = ({ target }) => {
  const [readingProgress, setReadingProgress] = useState(0);
    
  return <div className={`reading-progress-bar`} style={{width: `${readingProgress}%` }} />
};

Това е основата за нашия компонент. readingProgress ще се използва като ширина (в проценти) за нашата лента за напредък. Единствената опора за нашия компонент е target, която ще бъде препратка към нашия DOM контейнер на публикацията - повече за това след няколко минути.

Първо нека внедрим нашия слушател, който ще актуализира нашата лента за напредък при събития на превъртане:

const scrollListener = () => {
    if (!target.current) {
      return;
    }
const element         = target.current;
    const totalHeight     = element.clientHeight - element.offsetTop;
    const windowScrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
if (windowScrollTop === 0) {
      return setReadingProgress(0);
    }
if (windowScrollTop > totalHeight) {
      return setReadingProgress(100);
    }
    
    console.log(windowScrollTop);
setReadingProgress((windowScrollTop / totalHeight) * 100);
  };

windowScrollTop опитва куп различни стойности, които коригират undefined стойности за някои браузъри (напр. Safari).

Има един проблем с тази реализация: 100% напредък при четене се постига само ако сме превъртели покрай нашата цел. Малко вероятно е това да е вярно (освен че превъртате един ред, след като сте приключили с четенето на един ред, което би ви направило наистина странно) — така че трябва леко да коригираме начина, по който се изчислява напредъкът ни в четенето:

const totalHeight = element.clientHeight - element.offsetTop - window.innerHeight;

Това трябва да доведе до по-точен резултат по отношение на това кога лентата трябва да покаже завършена.

След това ще поставим нашия слушател в useEffect кука, което прави целия ни компонент да изглежда така:

const ReadingProgress = ({ target }) => {
  const [readingProgress, setReadingProgress] = useState(0);
  const scrollListener = () => {
    if (!target.current) {
      return;
    }
const element         = target.current;
    const totalHeight     = element.clientHeight - element.offsetTop - (window.innerHeight);
    const windowScrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
if (windowScrollTop === 0) {
      return setReadingProgress(0);
    }
if (windowScrollTop > totalHeight) {
      return setReadingProgress(100);
    }
setReadingProgress((windowScrollTop / totalHeight) * 100);
  };
  
  useEffect(() => {
    window.addEventListener("scroll", scrollListener);
    return () => window.removeEventListener("scroll", scrollListener);
  });
return <div className={`reading-progress-bar`} style={{width: `${readingProgress}%`}} />;
};

Върнатата функция от нашата кука useEffect е основно това, което се случва, когато компонентът е демонтиран (вижте Ефекти с почистване в документите).

Не на последно място трябва да използваме нашия компонент някъде. В този момент ще трябва да създадем реф на нашия целеви контейнер и просто да го предадем на нашия ReadingProgress компонент:

function App() {
  const target = React.createRef();
  return (
    <>
      <ReadingProgress target={target} />
      <div className={`post`} ref={target}>post content</div>
    </>
  );
}

Вижте документите за допълнителна информация относно createRef

Сега вашата лента за прогрес на четене трябва да работи перфектно - с изключение на това, че не можете да я видите, защото няма височина. Коригирайте това, като добавите малко CSS:

.reading-progress-bar {
  position: sticky;
  height: 5px;
  top: 0;
  background-color: #ff0000;
}

Готово! Сега вашите читатели вече не се губят в чистата безкрайна дължина на вашите публикации и винаги знаят кога ще свърши.

За да видите напълно работещ пример, можете да погледнете тази кодова писалка: https://codepen.io/nehalist/pen/agRNYZ

Пакети на трети страни

Има някои пакети на трети страни, които се справят точно с този проблем. Доколкото разбрах, повечето от тях са остарели и/или вече не се поддържат – но това, което е още по-уместно в този момент: наистина ли имате нужда от зависимост от трета страна за наистина прост компонент с около 30 реда код? Е, честно казано, не мисля така.

Заключение

Както видяхте, прилагането на лента за прогрес на четене в React е доста лесно. Благодарение на куките можем да внедрим този компонент като много малък функционален компонент с малко или никакви допълнителни разходи.

Ако имате някакви подобрения по отношение на внедряването или мислите, че това може да се направи по-добре, моля, уведомете ме в коментарите!

Ако сте харесали тази публикация, не се колебайте да оставите 👏, последвайте ме в Twitter и се абонирайте за моя бюлетин. Първоначално публикувано на https://nehalist.io на 9 юли 2019 г.