How to create a tabs-like menu but then slightly different in React app

Viewed 27

I have multiple pages in my React app where I want to have this functionality. So in a certain React page I add CopyToClipboard and the children TabIcon:

<CopyToClipboard>
  <TabIcon type="link" title="Test one" />
  <TabIcon type="embed" title="Test two" />
</CopyToClipboard>

In another page I maybe have only:

<CopyToClipboard>
  <TabIcon type="link" title="Test one" />
</CopyToClipboard>

The TabIcon component:

const TabIcon = ({ title, type, onClick }: Props) => {
  const theme = useTheme();
  const styles = Styles({ theme });

  return (
    <li>
      <ButtonBase type="button" onClick={onClick} title={title} disableRipple>
        <span css={styles.icon}>{type === 'embed' ? <EmbedIcon /> : <LinkIcon />}</span>
      </ButtonBase>
    </li>
  );
};

The CopyToClipboard component:

const CopyToClipboard = ({ children }: Props) => {
  const [isOpen, setIsOpen] = useState(false);
  const [isCopied, setIsCopied] = useState(false);

  const stringToCopy =
    type === 'link'
      ? `https://www.mysite${path}`
      : `<iframe>// iframe code etc here</iframe>`;

  const handleClick = () => {
    setIsOpen((current) => !current);
  };

  return (
    <div>
      <ul>
        {children.map((item) => (
          <TabIcon type={item.type} onClick={handleClick} />
        ))}
      </ul>
      {isOpen && (
        <div>
          <p>{stringToCopy}</p>
          <ButtonBase type="button" onClick={handleCopyClick} disableRipple>
            <span>{isCopied ? `${suffix} copied` : `Copy ${suffix}`}</span>
            <span>{isCopied ? <CheckIcon /> : <CopyIcon />}</span>
          </ButtonBase>
        </div>
      )}
    </div>
  );
};

export default CopyToClipboard;

So what I like to achieve is the following:

  • When clicking a button it toggles/shows the correct div with its associated content. When clicking the same button again it hides the div. When clicking the other button it shows the associated content of that button. When you click that button again it hides the div.

How to achieve this within my example?

1 Answers

You need a single state (text). If the text has a truthy value (actual text in this case), the div is displayed.

When you set the text, the handleClick compares the prevText and the new text (txt), and if they are equal it sets null as the value of text, which hides the div. If they are not equal, the new text is set, and the div is displayed.

const { useState } = React;

const Demo = () => {
  const [text, setText] = useState(null);

  const handleClick = txt => {
    setText(prevText => prevText === txt ? null : txt);
  };
  
  return (
    <div>
      <ul>
        <li>
          <button onClick={() => handleClick('Button 1 text')}>
            Button 1
          </button>
        </li>
        <li>
          <button onClick={() => handleClick('Button 2 text')}>
            Button 2
          </button>
        </li>
      </ul>
      {text && (
        <div>
          <p>{text}</p>
        </div>
      )}
    </div>
  );
}

ReactDOM.createRoot(root)
  .render(<Demo />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="root"></div>

Related