React change tab on changing the state

Viewed 3628

I'm trying to change the defaultActiveKey attribute of Tab in antd when click in other component. Below is the code to get active key and set tab.

import { Tabs } from "antd";

import React from "react";
const tabActiveContext = React.createContext({
  **setActivetab: (at) => {
  }**
});

class CustomTab extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      children:props.children,
      otherProps:props.otherProps,
      activetabkey: '1',
    };
    this.setActivetab = this.setActivetab.bind(this);
  }
  setActivetab() {
    this.setState({ activetabkey:'3' });
  }
  render() {
    **const { children, otherProps, activetabkey } = this.state;
    console.log(activetabkey)**;
    return (
          <div className="card-container">
            <Tabs
              centered
              **defaultActiveKey={activetabkey}**
              type="card"
              size={"small"}
              {...otherProps}
              tabBarStyle={{ marginBottom: 2 }}
            >
              {children}
            </Tabs>
          </div>
        );
  }

export default CustomTab;
export { tabActiveContext };

Below is the code to click on component and call to change using context.

 let { setActivetab } = useContext(tabActiveContext);
  const handleClickThumbnail = () => {
    setActivetab("3");
  };

I dont know how to bind context in class to change the state. Little help would be appreciated. In addition, I want to know the best way to change tab when I click on anything in other component. I've no idea about sharing state when there are some other props in that class from App component other than state. On the other, hand I've no idea how to bind context method in class constructor.

1 Answers

Essentially what you want to do is make the antd tabs controlled component by setting the activeKey prop on the Tabs.

Right now your tabActiveContext has a function to change the active tab, but it has no way to tell you what the active tab is. We want both things in the context.

const TabActiveContext = React.createContext({
  setActivetab: (at) => {},
  activeTab: "",
});

I'm assuming that you want to set the active tab from somewhere that's outside of this CustomTab component. So you need a context provider that contains the state of the active tab. This provider needs to be outside of both the CustomTab that you want to control and the component that you want to control it with.

export const TabActiveProvider = ({ children, initialActiveKey }) => {
  const [activeTab, setActiveTab] = useState(initialActiveKey);
  return (
    <TabActiveContext.Provider
      value={{
        activeTab,
        setActiveTab
      }}
    >
      {children}
    </TabActiveContext.Provider>
  );
};

Dealing with contexts is much easier in a function component. FYI there is no reason that you should store the props in the state -- just use the props!

const CustomTab = ({ children, ...props }) => {
  const { activeTab, setActiveTab } = useContext(TabActiveContext);
  return (
    <div className="card-container">
      <Tabs
        centered
        type="card"
        size={"small"}
        tabBarStyle={{ marginBottom: 2 }}
        {...props}
        activeKey={activeTab} // current tab is controlled by state
        onChange={setActiveTab} // update state when tab changes
      >
        {children}
      </Tabs>
    </div>
  );
};

Here is a full demo code showing how you can modify the tab from an external component.

import React, { useContext, useState } from "react";
import "antd/dist/antd.css";
import { Button, Tabs, TabsProps } from "antd";
const { TabPane } = Tabs;

export const TabActiveContext = React.createContext({
  setActiveTab: (at) => {},
  activeTab: ""
});

const CustomTab = ({ children, ...props }) => {
  const { activeTab, setActiveTab } = useContext(TabActiveContext);
  return (
    <div className="card-container">
      <Tabs
        centered
        type="card"
        size={"small"}
        tabBarStyle={{ marginBottom: 2 }}
        {...props}
        activeKey={activeTab} // current tab is controlled by state
        onChange={setActiveTab} // update state when tab changes
      >
        {children}
      </Tabs>
    </div>
  );
};

export const TabActiveProvider = ({ children, initialActiveKey }) => {
  const [activeTab, setActiveTab] = useState(initialActiveKey);
  return (
    <TabActiveContext.Provider
      value={{
        activeTab,
        setActiveTab
      }}
    >
      {children}
    </TabActiveContext.Provider>
  );
};

// some component that modifies the tab state
const OtherComponent = () => {
  const { setActiveTab } = useContext(TabActiveContext);

  return <Button onClick={() => setActiveTab("2")}>Go To Tab #2</Button>;
};

export default () => (
  <TabActiveProvider initialActiveKey="1">
    <OtherComponent />
    <CustomTab>
      <TabPane tab="Tab 1" key="1">
        Content of Tab Pane 1
      </TabPane>
      <TabPane tab="Tab 2" key="2">
        Content of Tab Pane 2
      </TabPane>
      <TabPane tab="Tab 3" key="3">
        Content of Tab Pane 3
      </TabPane>
    </CustomTab>
  </TabActiveProvider>
);

CodeSandbox Link

Related