DEV Community

Radu Breahna
Radu Breahna

Posted on

Dynamic Page Title with React Hooks

This post is a part of a series about the ins and outs of chroniker.co

Without going into detail about what chroniker.co is actually for. I will try to illustrate how I added some very small but useful feature to the browser tab in the form of a dynamic title.

When the site tab is not active it's actually hard to understand if the time is running. I wanted some kind of activity indicator that would require only a glance from the user and no further interaction.

Seeing as the browser tab is always visible, the logical solution was to add something to the title.

Contextually it had to be the ⌛ emoji.

I had a flag saved somewhere in state that told me if the app was running, so all that was left was to add the emoji to the beginning of the title when the flag was 1 and remove it when it was 0. Another thing I wanted to achieve was to remove it if the user navigated to a different page, where it was no longer relevant.

chroniker.co is entirely written with react hooks (with small exceptions from third party packages). Naturally I had to wrap this feature in a hook as well.

Here is what came of it:

import { useEffect, useState } from 'react';

const useTitleActivity = (isActive = 1, sequenceActive = '\u23F3', sequenceInactive = '') => {
  const [title, setTitle] = useState(document.title);

  //  Provides consistent string across entire hook
  function getInsertedSequence(sequence) {
    return sequence === '' ? '' : `${sequence} `;
  }

  function cleanTitle(currentTitle) {
    return currentTitle
      .replace(new RegExp(`${getInsertedSequence(sequenceActive)}|${getInsertedSequence(sequenceInactive)}`, 'g'), '');
  }


  useEffect(() => {
    document.title = title;
  }, [title]);

  useEffect(() => {
    let newTitle = cleanTitle(title);
    if (isActive === 1) {
      newTitle = `${getInsertedSequence(sequenceActive)}${newTitle}`;
    } else if (isActive === 0) {
      newTitle = `${getInsertedSequence(sequenceInactive)}${newTitle}`;
    }

    setTitle(newTitle);

    return () => {
      document.title = cleanTitle(title);
    };
  }, [isActive, sequenceActive, sequenceInactive]);
};

export default useTitleActivity;

Enter fullscreen mode Exit fullscreen mode

The hook is called useTitleActivity and I use it like this:


useTitleActivity(state.isActive);

Enter fullscreen mode Exit fullscreen mode

Inside it works by first setting the current title in its local state:

const [title, setTitle] = useState(document.title);
Enter fullscreen mode Exit fullscreen mode

The following:

function getInsertedSequence(sequence) {
    return sequence === '' ? '' : `${sequence} `;
  }
Enter fullscreen mode Exit fullscreen mode

is just a local helper that makes sure I insert either exactly what I need or an empty string. In hindsight this might have been overkill

Moving on...

function cleanTitle(currentTitle) {
    return currentTitle
      .replace(new RegExp(`${getInsertedSequence(sequenceActive)}|${getInsertedSequence(sequenceInactive)}`, 'g'), '');
  }
Enter fullscreen mode Exit fullscreen mode

The goal of this function is to remove any foreign elements from the initial title, nothing else.

useEffect(() => {
    document.title = title;
  }, [title]);
Enter fullscreen mode Exit fullscreen mode

This effect will set the document.title with the title from the hook's local state every time that title changes.

And Finaly:

  useEffect(() => {
    let newTitle = cleanTitle(title);
    if (isActive === 1) {
      newTitle = `${getInsertedSequence(sequenceActive)}${newTitle}`;
    } else if (isActive === 0) {
      newTitle = `${getInsertedSequence(sequenceInactive)}${newTitle}`;
    }

    setTitle(newTitle);

    return () => {
      document.title = cleanTitle(title);
    };
  }, [isActive, sequenceActive, sequenceInactive]);
};
Enter fullscreen mode Exit fullscreen mode

I found it easier to sometimes read hook effects from their end as you can clearly see their dependencies.

In this case, every time either isActive, sequenceActive or sequenceInactive change, a few things need to happen.

First we get our clean title.

Then we prefix it with our 'Active' or 'Inactive' emoji depending on the activity flag.

We then set the resulting title in the hook's state, this will make sure to trigger the effect above and sync the local state with the page title.

Finally, in case the user navigates away from the page where this hook is active, we revert the title to its unaltered form.

That's It. It's a small feature although very useful for the end user. Feel free to suggest rewrites/alternatives that would improve the code above.

And if you ever need a pwa for free and easy time tracking then you already know where to find one

Top comments (0)