DEV Community

Cover image for Dynamic Item Selection: Made with React Hooks
Edgar Carrillo
Edgar Carrillo

Posted on

Dynamic Item Selection: Made with React Hooks

TLTR

Jump to Quick Explanation below to get a 15 second explanation.

Just want to see it in action? Try out the deployed demo. Want a deep dive? Take a peek at the repo.

Requirements

You're required to know the below in order to understand what I'm going to be explaining in this article:

  • React Hooks
  • React Functional Components
  • Styled-Components

Understand the above? Great, keep reading! If you don't I'd do some quick googling to learn about them or for a refresher.

Quick Explanation

This Hook allows the user to create an 'n' number of list items that on hover will emphasize the hovered list item and dim all other elements.

When the user no longer is hovering over a list item all elements will return to the default coleration.

This hook also pairs with a styled component, in this case it's called TextAnimateLi which is hard coded to fit my styling needs but can be changed to your style and element preference.

useTextAnimate

const useTextAnimate = (data) => {
  const [content, setContent] = useState(data);

  const setOneActive = (name) => {
    let contentCopy = [...content];

    contentCopy.forEach((item, index, arr) => {
      if (arr[index].name !== name) {
        arr[index].isActive = false;
      }
    });

    setContent(contentCopy);
  };

  const setAllActive = () => {
    let contentCopy = [...content];

    contentCopy.forEach((item, index, arr) => {
      arr[index].isActive = true;
    });

    setContent(contentCopy);
  };

  return { setOneActive, setAllActive, content };
};
Enter fullscreen mode Exit fullscreen mode

This hook when used takes in an array of objects as it's argument.

const data = useTextAnimate([
    { name: 'work', isActive: true },
    { name: 'about', isActive: true },
]);
Enter fullscreen mode Exit fullscreen mode

From here, we're assigning the array of objects data to content in state. This allows us to use stateful logic needed for dynamic style changing.

Then we come onto our first function setOneActive which will set all elements but the one with the matching name to false. This is the logic that allows us to see one element as emphasized.

The following function setAllActive() will set all items to be emphasized which is the default logic.

what is returned is:

  • content - array of objects that the user provided.
  • setOneActive - function
  • setAllActive - function

Real Life use

When using the hook it will take in an array of objects as an argument.

Each object must contain the following properties

  • name (Init with the text you want in the list item)
  • isActive (set it to true by default always)
const data = useTextAnimate([
    { name: 'work', isActive: true },
    { name: 'about', isActive: true },
    { name: 'contact', isActive: true },
    { name: 'cv', isActive: true },
  ]);
Enter fullscreen mode Exit fullscreen mode

Note: the value retrieved from useTextAnimate must be assigned to a variable.

useTextContent will return 3 things.

  • content (the array of objects from earlier)
  • setOneActive (Explained in useTextAnimate above)
  • setAllActive (Explained in useTextAnimate above)

The hook provided the logic needed now we are going to populate a unordered list with list items that will use that logic.

Before we start using the logic we are going to need the styled-component TextAnimateLi.

const TextAnimateLi = styled.li`
  color: white;
  transition: color 0.2s;
  cursor: pointer;
  ${({ content, index }) => {
    if (content[index].isActive === false) {
      return `
      color: rgba(255, 255, 255, 0.5);  
    `;
    }
  }}
`;

Enter fullscreen mode Exit fullscreen mode

To keep it short and sweet. It uses data provided by useTextAnimate to style each list item dynamically.

Now in order to put this together, we need to map over the array we created in my example, we can do this with data.content (Remember, using the variable name data was a personal choice when creating the variable earlier. It can be anything you'd like!)

<ul className={className}>
      {data.content.map((item, index) => {
        return (
          <TextAnimateLi
            key={index}
            onMouseEnter={() => data.setOneActive(item.name)}
            onMouseLeave={() => data.setAllActive()}
            content={data.content}
            index={index}
          >
            {item.name}
          </TextAnimateLi>
        );
      })}
    </ul>
Enter fullscreen mode Exit fullscreen mode

What's going on here? The parameter item in the arrow function is the current object within the array content.

For each Component TextAnimateLi we are adding a set of properties.

  • key
    MUST take index as it's value, do NOT use something like uniqid()).

  • onMouseEnter
    Calls the function setOneActive()

  • onMouseLeave
    Calls the function setAllActive()

  • content
    takes in the array of objects

  • index
    Takes the current index

Let's take a look back at TextAnimateLi to understand the styling logic.

/* It doesn't have to be a li. But for this case it's the best option. */
const TextAnimateLi = styled.li`
  /* This is the default color */
  /* You can set a new color here */
  color: white;
  transition: color 0.2s;
  cursor: pointer;
  ${({ content, index }) => {
    if (content[index].isActive === false) {
      return `
      /* This is the dimming color */
      /* You can set a new color here */
      color: rgba(255, 255, 255, 0.5);  
    `;
    }
  }}
`;
Enter fullscreen mode Exit fullscreen mode

Pretty straight forward, when the current item is not active it will return a color which is dimmed otherwise, it will return to it's default color.

Take a look at my comments in the code to make changes as you see fit. feel free to mess around with the styling for different looks!

Enjoy!

Top comments (0)