0

I am new to React.js, I know basic stuff like state, components, but I need to: Create a MyTabsComponent to be used like this:

<MyTabsComponent>
  <div title={"Section title 1"}>Content of section 1</div>
  <div title={"Section title 2"}>Content of section 2</div>
  <div title={"Section title 3"}>Content of section 2</div>
  <div title={"Section title 4"}>Content of section 2</div>
  <div title={"Section title 5"}>Content of section 2</div>
  
  .
  .
  .
  .
  .and so on..............
  .
  .
  .
  .
  .
  
</MyTabsComponent>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

See visual

The above code should render like this:

<div class="tabs">
        <button class="btn-active">Section title 1</button>
        <button class="btn">Section title 2</button>
        <button class="btn">Section title 3</button>
        <button class="btn">Section title 4</button>
        <!--
        
        .
        .
        .
        .
        .
        .
        and so on..........
        -->
        
        <div class="view">
            Content of section 1
        </div>
    </div>

What I've tried:

import React,{useState} from "react";

const MyTabsComponent = () => {
    const [title,setTitle] = useState(['Section 1','Section 2','Section 3']);
    const [ct,setCt] = useState(null);

    return (
        <div className="tabs">
        <TabButtons tabName={title} tabCt={setCt} />
        <div class="view">
           Content : {ct}
             </div>
    </div>
    );
};

const TabButtons = (props) => {
    return (
          <>
          {
              props.tabName.map((item,i)=>
              <button onClick={()=>props.tabCt(i)} className="btn">{item}</button>

              ) 
          }
            
            </>
    );
};

export default MyTabsComponent;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

I don't know how to do this for any number of tabs. Rest I can handle-- CSS, onClick events I understand well, as I know JS well.

EDIT: I found an article on Compound Components https://blog.logrocket.com/understanding-react-compound-components/

and they say:

import React, { useState, createContext, useContext } from "react";

//the name of this context will be DataContext
const DataContext = createContext({});

function Tab({ id, children }) {
  //extract the 'setActiveTabID` method from the DataContext state.
  const [, setActiveTabID] = useContext(DataContext);
  return (
    <div>
      <div onClick={() => setActiveTabID(id)}>{children}</div>
    </div>
  );
}
function TabPanel({ whenActive, children }) {
  //get the 'activeTabID' state from DataContext.
  const [activeTabID] = useContext(DataContext);
  return <div>{activeTabID === whenActive ? children : null}</div>;
}

function TabSwitcher(props) {
  const [activeTabID, setActiveTabID] = useState("a");
  //since this component will provide data to the child components, we will use DataContext.Provider
  return (
    <DataContext.Provider value={[activeTabID, setActiveTabID]}>
      {props.children}
    </DataContext.Provider>
  );
}

export default TabSwitcher;
export { Tab, TabPanel };

And to use:

import TabSwitcher, { Tab, TabPanel } from "./TabSwitcher";

function App() {
  return (
    <div className="App">
      <h1>TabSwitcher with Compound Components</h1>
      <TabSwitcher>
        <Tab id="a">
          <button>a</button>
        </Tab>
        <Tab id="b">
          <button>b</button>
        </Tab>

        <TabPanel whenActive="a">
          <div>a panel</div>
        </TabPanel>

        <TabPanel whenActive="b">
          <div>b panel</div>
        </TabPanel>
      </TabSwitcher>
    </div>
  );
}
export default App;

The problem is: they are using

TabPanel

as a container whereas I want a <div>

2 Answers 2

2

EDIT: Please see the link Alexander posted in the comment for the best practices in accessibility for tabs like this, or this link which has an example https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-2/tabs.html

My post here solves the problem in terms of "making this work", but you should really be aiming for proper tabs and tabpanels to make this accessible.

ORIGINAL ANSWER:

The problem is: they are using TabPanel as a container whereas I want a <div>

When you say "I want a <div>", do you mean that you want to write div in your code, or that you want a div in the DOM?

The example you found, where they use TabPanel, does render a div to the DOM, because TabPanel returns a div.

One thing that jumps out is you don't need to store your Titles in state, because they are never changing.

To do what you're trying to do, I'd probably go like this:

import React,{useState} from "react";

const tabData = [
  {title: "Section title 1", content: "Content of section 1"}
  {title: "Section title 2", content: "Content of section 2"}
  {title: "Section title 3", content: "Content of section 3"}
]

const MyTabsComponent = () => {
    const [ct,setCt] = useState(null);

    return (
        <div className="tabs">
         {tabData.map(({title}, i) => (
           <TabButton
              title={title}
              isActive={i === ct}
              onClick={()=>props.tabCt(i)} />
         )}       
          <div class="view">
           Content : {tabData[ct].content}
          </div>
        </div>
    );
};

const TabButton = ({ title, isActive, onClick }) => {
    return (
            <button onClick={onClick} className={isActive ? "active-btn" : "btn"}>
              {title}
            </button>
    );
};

export default MyTabsComponent;
Sign up to request clarification or add additional context in comments.

2 Comments

Just an item of note-- tab widgets need to be authored in a very specific way to make them properly accessible to users leveraging keyboards for navigation, screen readers, or other assistive technologies. For more info, see the WAI-ARIA Authoring Practices section on building tabs.
Thanks @AlexanderNied I've updated my answer to make it clear this it not a properly accessible way to to this in html.
0

From what I can tell, your question is how to render any number of tabs? It seems that your snippet already does this. However, you did have one too many parentheses, or one too few depending on how you look at it.

import React,{useState} from "react";

const myTabs = ['Section 1','Section 2','Section 3'];

const MyTabsComponent = () => {
    const [ct,setCt] = useState(null);

    return (
      <div className="tabs">
        <TabButtons tabCt={setCt} />
        <div>Content: {ct}</div>
      </div>
    );
};

const TabButtons = (props) => {
    return (
      <>
      {myTabs.map((item,i) => (
        <button onClick={()=>props.tabCt(i)} className="btn">
          {item}
        </button>
      )}
      </>
    );
};

export default MyTabsComponent;

This should work. You could have 1 or 1000 tabs in myTabs and it would render all of them.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.