meez
meez

Reputation: 4769

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

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:

How to achieve this within my example?

Upvotes: 1

Views: 158

Answers (1)

Ori Drori
Ori Drori

Reputation: 191976

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>

Upvotes: 1

Related Questions