React-Bootstrap - How to activate a Tab outside NavItem - reactjs

I want to change tab without clicking on the NavItem evtKey="x"
If I have a TabContainer like this :
<Tab.Container id="tabcontainer" defaultActiveKey="a">
<Tab.Content animation>
<Tab.Pane eventKey="a">
<ComponentA />
</Tab.Pane>
<Tab.Pane eventKey="b">
<Componentb />
</Tab.Pane>
</Tab.Content>
<Nav stacked bsStyle="pills" pullLeft>
... NavItems ...
</Nav>
<Tab.Container>
I want to know how I can do this :
eventHandler(){
changeToTab("b")
}
inside ComponentA.

The tab container, replaced defaultKey with activeKey={this.state.key}, and managed the state of the parent with a function, passed as a prop to ComponentA.
on the parent of the Tab.Container
handleSelect(key){
this.setState({ key : key })
}
render() {
... render stuff ...
return (
<Tab.Container id="tabcontainer" activeKey={this.state.key}>
<Tab.Content animation>
<Tab.Pane eventKey="a">
<ComponentA changeTab={this.handleSelect}/>
</Tab.Pane>
<Tab.Pane eventKey="b">
<ComponentB />
</Tab.Pane>
</Tab.Content>
<Nav stacked bsStyle="pills" pullLeft>
... NavItems ...
</Nav>
<Tab.Container>
)
}
And on the Component A
eventHandler(){
this.props.changeTab("b")
}

Related

How to add if else inside the Tabs in react bootstrap

I try to execute following code but I got some error.
Error : ReactBootstrap: The Tab component is not meant to be rendered! It's an abstract component that is only valid as a direct Child of the Tabs Component. For custom tabs components use TabPane and TabsContainer directly.
import { Row, Col, Tabs, Tab } from "react-bootstrap";
import One from "../One.jsx";
//One,Two .. etc There are more
render(
<Tabs>
<Tab eventKey={1} title="Tab 1"><One/></Tab>
<Tab eventKey={2} title="Tab 2"><Two/></Tab>
<Tab eventKey={3} title="Tab 3"><Three/></Tab>
{this.state.type === type && (
<Tab eventKey={4} title="Tab 4"><Four/></Tab>
<Tab eventKey={5} title="Tab 5"><Five/></Tab>
<Tab eventKey={6} title="Tab 6"><Six/></Tab>
)}
</Tabs>
)
I found this solution by accident.
I used to declare component's name as Tab without import from 'react-bootstrap' and using JSX condition rendering not any problem
But when I changed component's name to another, I found error from 'react-bootstrap' and my JSX condition rendering didn't work anymore cause 'react-bootstrap' wont accept JSX shorthand fragment or any.
So I declare empty component for handle error and using <>...</> in JSX condition rendering and everything works for me.
import React from "react";
import { Tabs } from "react-bootstrap";
const Tab = () => {
return <></>;
};
export const App = (props) => {
return(
<Tabs>
<Tab eventKey={1} title="Tab 1"><One/></Tab>
<Tab eventKey={2} title="Tab 2"><Two/></Tab>
<Tab eventKey={3} title="Tab 3"><Three/></Tab>
{props.type === type && (
<>
<Tab eventKey={4} title="Tab 4"><Four/></Tab>
<Tab eventKey={5} title="Tab 5"><Five/></Tab>
<Tab eventKey={6} title="Tab 6"><Six/></Tab>
</>
)}
</Tabs>
)
}
On top is not good practice but it's easy to declare condition.
In case you want to do with better practice use this instead.
import React from "react";
import { Tabs, Tab } from "react-bootstrap";
export const App = (props) => {
return(
<Tabs>
<Tab eventKey={1} title="Tab 1"><One/></Tab>
<Tab eventKey={2} title="Tab 2"><Two/></Tab>
<Tab eventKey={3} title="Tab 3"><Three/></Tab>
{props.type === type && <Tab eventKey={4} title="Tab 4"><Four/></Tab>}
{props.type === type && <Tab eventKey={5} title="Tab 5"><Five/></Tab>}
{props.type === type && <Tab eventKey={6} title="Tab 6"><Six/></Tab>}
</Tabs>
)
}
I don't think you can construct new Tab components like that using conditional rendering. As stated in the error message, you can use Tab.Container & Tab.Pane to create your customs Tabs.
Reference to the docs:
For more complex layouts the flexible TabContainer, TabContent, and
TabPane components along with any style of Nav allow you to quickly
piece together your own Tabs component with additional markup needed.
Example implementation of Tabs contruction with conditional rendering:
return (
<Tab.Container defaultActiveKey={1}>
<Nav variant="pills" className="flex-column">
<Row>
<Nav.Item>
<Nav.Link eventKey={1}>Tab 1</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey={2}>Tab 2</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey={3}>Tab 2</Nav.Link>
</Nav.Item>
{this.state.type === type && (
<>
<Nav.Item>
<Nav.Link eventKey={4}>Tab 4</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey={5}>Tab 5</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey={6}>Tab 6</Nav.Link>
</Nav.Item>
</>
)}
</Row>
</Nav>
<Tab.Content>
<Tab.Pane eventKey={1} title="Tab 1">
1
</Tab.Pane>
<Tab.Pane eventKey={2} title="Tab 2">
2
</Tab.Pane>
<Tab.Pane eventKey={3} title="Tab 3">
3
</Tab.Pane>
{this.state.type === type && (
<>
<Tab.Pane eventKey={4} title="Tab 4">
4
</Tab.Pane>
<Tab.Pane eventKey={5} title="Tab 5">
5
</Tab.Pane>
<Tab.Pane eventKey={6} title="Tab 6">
6
</Tab.Pane>
</>
)}
</Tab.Content>
</Tab.Container>
);
In fact, if you view the components on React Dev Tools, you will notice that the "Tab" component in fact is not actually rendered when using Tabs & Tab combo - hence they defined it as "abstract" component
Alternatively, if you must use Tabs & Tab JSX combo, then you are going to have to implement the condition on the entire Tabs rendering - not just a section of it. You can find an example here: https://codesandbox.io/s/beautiful-pond-quc2b?file=/src/App.js
You can refer to the following official documentation. It is explained very well.
https://reactjs.org/docs/conditional-rendering.html
Here is a sample code which I took from documentation. Hope this will clear your concept.
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}

How to add as attribute and get React Konva Element ID

I would like to know how to add an ID to an element and retrieve it similarly to e.target.getAttribute("id") for DOM elements. One thing to note is that the elements are based on an array that is part of the parent component. In order for the elements to render based on the parent's array, I added the and at parent component while and are at the child's. In short, it is structured this way.
Parent Class:
<div class="designScreen" onMouseDown={this.activateRouteTrigger} onMouseUp={this.deactivateRouteTrigger} onMouseMove={this.updateRoutePosition} >
<Stage
width={900}
height={700}>
<Layer>
{
this.state.trends.map((el) => {
return <Content id={el.id}
element={el.element}
color={el.color}
title={el.title}
type={el.type}
visibilityStatus={el.visibilityStatus}
selectType={this.selectType}
parentCallback = {this.selectDestinations}
selectedDestinationID = {this.state.selectedDestinationID}
destinations={this.state.destinations}
parentCallbackColor = {this.changeColorSelector}
timerString={this.state.timerString}
/>
})
}
</Layer>
</Stage>
</div>
Child Class:
render(){
const ex1= <Group >
<Rect name="name" onClick={this.selectedRouteID} width={10} height={10} draggable={true} fill="green"></Rect>
<Arrow id={`${this.props.element} content`} onClick={this.selectedRouteID} points={[700,300,200,300]} pointerLength={20} zIndex={5} draggable="true"
pointerWidth={20} fill={`${this.props.color}`} stroke={`${this.props.color}`} strokeWidth={4} />
</Group>
const ex2=
<Arrow points={[100,300,600,300]} pointerLength={20} zIndex={5} draggable="true"
pointerWidth={20} fill={`${this.props.color}`} stroke={`${this.props.color}`} strokeWidth={4} />
return(
this.props.element==="line" ? ex1 : ex2
)
}
}
I solved it, use e.target.getAttrs().fill, to get fill and replace it for other attributes

How to call an event on tabs changed in react-bootstrap

I've implemented Navigation Tabs in my React application using React-Bootstrap.
Like this:
<Tabs defaultActiveKey={1}>
<Tab eventKey={1} title="Log in">
{/* Irrelevant code */}
</Tab>
<Tab eventKey={2} title="Sign up">
{/* Irrelevant code */}
</Tab>
</Tabs>
Now on changing tabs I would like to call the following funtion:
changeTab(login) {
if (login)
this.setState({ heading: "Log in" })
else
this.setState({ heading: "Sign up" })
}
Where login is a Boolean that will be true for when the Log in tab is selected and false when the Sign up tab is selected.
How can I do that?
Edit:
I've figured out that you can call a function on when the tabs are clicked like this:
<Tabs defaultActiveKey={1} onClick={()=>this.changeTab()}>
<Tab eventKey={1} title="Log in">
{/* Irrelevant code */}
</Tab>
<Tab eventKey={1} title="Sign up">
{/* Irrelevant code */}
</Tab>
</Tabs>
But how can I know which tab was clicked? I need it to change the state based on which tab is clicked.
You need to use onSelect in the Tabs component.
Like this:
<Tabs defaultActiveKey={1} onSelect={this.handleSelect()}>
<Tab eventKey={1} title="Log in">
{/* Irrelevant code */}
</Tab>
<Tab eventKey={2} title="Sign up">
{/* Irrelevant code */}
</Tab>
</Tabs>
And then make this your handleSelect function:
handleSelect(key) {
if (key === 1)
this.setState({ heading: "Log in" })
else
this.setState({ heading: "Sign up" })
}
<Tab.Container id="" defaultActiveKey={key} onSelect={this.handleSelect}>
<Nav variant="tabs">
<Nav.Item>
<Nav.Link eventKey={'1'}>Staking</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey={'2'}>Providers</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey={'3'}>Overview</Nav.Link>
</Nav.Item>
</Nav>
<Tab.Content>
<Tab.Pane eventKey={'1'}>
<Row>
<Col>
1111
</Col>
</Row>
</Tab.Pane>
</Tab.Content>
<Tab.Content>
<Tab.Pane eventKey={'2'}>
<div>22222</div>
</Tab.Pane>
</Tab.Content>
<Tab.Content>
<Tab.Pane eventKey={'3'}>
<div>333</div>
</Tab.Pane>
</Tab.Content>
</Tab.Container>
Now in your state add 'key' field with initial value '1'
constructor(props) {
super(props);
this.state = {
key: '1'
};
}
Then write a function
handleSelect = (key) => {
console.log(key,';;',typeof key)
if(key === '1') {
console.log('ll', key)
} else if(key === '2') {
console.log('ll', key)
} else if(key === '3') {
console.log('ll', key)
}
}
Initially when the page loades, the function is not called so you can make API calls or any thing you want to do in the 1st tab, in componentDidMount or any other function and place it in the 1st tab i.e where I've written 1111,after this when you click another tab, handleSelect function is called and based on key value with use if..else statement to perform specific task for specific tab.
Hope this helps. If any issue please let us know in the comment section.
Here, You can use onSelect as Barry Michael Doyle said. Let me post you a sample code to make it more clear for you.
function Example() {
return (
<Tabs defaultActiveKey="home" id="uncontrolled-tab-example"
className="mb-3" onSelect={(e) => handleClick(e)}>
<Tab eventKey="homeTab" title="Home">
</Tab>
<Tab eventKey="profileTab" title="Profile">
</Tab>
<Tab eventKey="contactTab" title="Contact">
</Tab>
</Tabs>
);
function handleClick(e) {
console.count(e);
//result will be the eventKey of the tab you clicked on.
// `homeTab` (when clicked on home tab)
// `profileTab` (when clicked on profile tab)
// `constactTab` (when clicked on Contact tab)
}
}

Dynamic EventKey not working React-Bootstrap

I am dynamically rendering my tabs in React-Bootstrap, but somehow the eventKey doesn't recognise the values and the tabs aren't clickable.
Here's the code.
var tabButtons = tabsList.map(function(text, index){
if(text.enabled==1)
{
return <NavItem eventKey={index} key={index}><center> {text.value}</center></NavItem>; }
});
Here tabList is an array which contains information about whether the button is enabled. If yes, then display the button information.
The code where I am calling these tabs.
render() {
return (
<div>
<Tab.Container id="tab" defaultActiveKey="0">
<Row className="show-grid">
<Col sm={12}>
<Nav bsStyle="tabs" justified>
{tabButtons}
</Nav>
</Col>
<Col sm={12}>
<Tab.Content animation>
<Tab.Pane eventKey="0">
<FirstTabContent />
</Tab.Pane>
<Tab.Pane eventKey="1">
Second
</Tab.Pane>
<Tab.Pane eventKey="2">
<ThirdTabContent />
</Tab.Pane>
</Tab.Content>
</Col>
</Row>
</Tab.Container>
</div>
);
I have no idea, why aren't the eventKey able to use the index values.
Anything I am missing? Another approach perhaps?
Apparently after some hit and tries, got it working.
The problem was indeed in eventKey.
Replaced eventKey="0" with eventKey={0}
Though I have no idea what difference it makes. Anyone?

Convert class component (with local state) to stateless functional component & container

I am new to react-redux and currently working on a project where we have a mandate from our software architecture team to write all our components as stateless functional components (introduced in React 0.14) and whenever we need to pass them a piece of the redux state we should use containers.
My questions are:
Is this pattern applicable for every component and how? Even for class based components that have their own "state" (not redux state)? Is it possible for all class components to be rewritten as stateless functional components plus a container?
The reason I am asking is more specific actually:
The team has decided to use reactstrap-tabs to implement tab functionality inside our components. The tabs will live inside a parent component and inside each tab a different child component will be displayed. From the documentation it seems that the parent tabbed component should be implemented as a class based component (handling this.state). Is it possible to rewrite this as a stateless functional component and a container?
Any help would be really appreciated since I am stuck on this. Here is the sample code...
import React from 'react';
import { TabContent, TabPane, Nav, NavItem, NavLink, Card, Button, CardTitle, CardText, Row, Col } from 'reactstrap';
import classnames from 'classnames';
import MyFirstChildComponent from '/components/MyFirstChildComponent';
import MySecondChildContainer from '/containers/MySecondChildContainer';
export default class TabbedParent extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
activeTab: '1'
};
}
toggle(tab) {
if (this.state.activeTab !== tab) {
this.setState({
activeTab: tab
});
}
}
render() {
return (
<div>
<Nav tabs>
<NavItem>
<NavLink
className={classnames({ active: this.state.activeTab === '1' })}
onClick={() => { this.toggle('1'); }}
>
MyFirstTab
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={classnames({ active: this.state.activeTab === '2' })}
onClick={() => { this.toggle('2'); }}
>
MySecondTab
</NavLink>
</NavItem>
</Nav>
<TabContent activeTab={this.state.activeTab}>
<TabPane tabId="1">
<Row>
<Col sm="12">
<MyFirstChildComponent />
</Col>
</Row>
</TabPane>
<TabPane tabId="2">
<Row>
<Col sm="6">
<MySecondChildContainer />
</Col>
</Row>
</TabPane>
</TabContent>
</div>
);
}
}
It sounds like your architecture team wants you to keep state centralized in a Redux store, and you are asking if you can use components which maintain their own state. There's obviously a conflict there, but you can achieve what you want with the tabs by keeping the tab state in the store, using containers to update the tabs when they're toggled, and dispatching actions then the tabs are selected.
If you can't make changes to the structure of the store for your UI, then I suppose you could only make design and component choices that would work with the data available to you in the store. I'm not sure how inflexible your architecture team is.
A simple implementation of your example might look like this:
import MySecondChildContainer from '/containers/MySecondChildContainer';
const TabbedParent = ({activeTab, clickHandler}) =>
<div>
<Nav tabs>
<NavItem>
<NavLink
className={classnames({ active: activeTab === '1' })}
onClick={() => clickHandler('1')}
>
MyFirstTab
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={classnames({ active: activeTab === '2' })}
onClick={() => clickHandler('2')}
>
MySecondTab
</NavLink>
</NavItem>
</Nav>
<TabContent activeTab={activeTab}>
<TabPane tabId="1">
<Row>
<Col sm="12">
<MyFirstChildComponent />
</Col>
</Row>
</TabPane>
<TabPane tabId="2">
<Row>
<Col sm="6">
<MySecondChildContainer />
</Col>
</Row>
</TabPane>
</TabContent>
</div>
const mapStateToProps = (state) => ({
activeTab: state.activeTab,
});
const mapDispatchToProps = dispatch => ({
clickHandler(id) { dispatch(TabClicked(id)) },
});
export connect(mapStateToProps, mapDispatchToProps)(TabbedParent);

Resources