Criando um simples Tabs component em React
Já faz algum tempo que não falo nada sobre React, e por conta que venho trabalhando tanto com React quanto Angular nos ultimos meses, acho que seria interessante ver como podemos criar composição de componentes utilizando uma estrutura simples a qual ira nos permitir reutilizar-los de maneira simples.
A minha motivação para escrever este artigo foi que em 2 meses, minha equipe e eu reescrevemos nossos componentes que utilizamos em várias aplicações, afim de diminuir a duplicação de código e também para revisarmos as dependências de terceiros de vários projetos que utilizamos atualmente.
Inicialmente utilizávamos muitas dependências do Bootstrap o qual provavelmente foi muito útil no início do projeto. Com o tempo, além de nos custar um pouco de trabalho sobrescrever os estilos dos componentes do Bootstrap, também criávamos outros componentes a partir dos elementos que já existiam.
Além disso, tínhamos (ainda temos) muitos componentes open-source para coisas que não queríamos criar do zero, mas hoje com uma maior maturidade decidimos reescrever algumas partes do software para termos mais controle sobre aquilo que desenvolvemos.
Mantendo a simplicidade
Com objetivo de mostrar como algo simples pode ser feito, vou criar um componente para <Tabs/>
que seja flexível para renderizar qualquer jsx
como conteúdo da `
Vamos então desenvolver algo assim:
<Tabs>
<Tab label={'Tab #1'} className={'tab-custom-class'}>
<p>content 0</p>
</Tab>
<Tab label={'Tab #2'} className={'tab-custom-class'}>
<CustomComponent propA={'foo'} propB={'bar'} propC={this.handleEvent}/>
</Tab>
</Tabs>
Como podemos ver, <Tab/>
deve renderizar qualquer jsx
válido. Isso nos traz a flexibilidade de utilizar propriedades customizadas para outras funcionalidades que sejam úteis no futuro.
O componente
O componente <Tabs/>
gerencia seu próprio state e sabe qual conteúdo renderizar baseado na <Tab/>
selecionada, sendo assim, ele é quem irá mostrar ou esconder conteúdo. Isso também nos traz a capacidade de renderizar uma tab por padrão quando nenhuma for selecionada através do state interno.
Obs: Você pode encontrar o link com o código e o demo no final deste artigo.
class Tabs extends Component {
constructor(props) {
super(props);
this.state = { activeIndex: props.defaultActiveIndex || 0 };
}
handleTabClick = (tabIndex) => {
if (tabIndex !== this.state.activeIndex) {
this.setState({ activeIndex: tabIndex });
}
}
cloneTabElement = (tab, index = 0) => {
const { activeIndex } = this.state;
return (
cloneElement(tab, {
onClick: () => this.handleTabClick(index),
tabIndex: index,
isActive: index === activeIndex,
})
);
}
renderChildrenTabs = () => {
const { children } = this.props;
if (!Array.isArray(children)) {
return this.cloneTabElement(children);
}
return children.map(this.cloneTabElement);
}
renderChildrenTabs = () => {
const { children } = this.props;
const { activeIndex } = this.state;
if (children[activeIndex]) {
return children[activeIndex].props.children;
}
return children.props.children;
}
render() {
const { className } = this.props;
return (
<section className={`tabs ${className}`}>
<ul className={'tabs__list'}>
{this.renderChildrenTabs()}
</ul>
<div className={'tabs__content'}>
{this.renderActiveTabContent()}
</div>
</section>
);
}
};
O componente
O componente <Tab/>
é stateless (o que nos permite utilizar um componente funcional), ou seja, o que além de não possuir state
, possui o único objetivo de renderizar a tab, permitindo que o desenvolvedor especifique o estilo, e executar uma ação onClick
relacionada ao escopo pai (o componente <Tabs/>
) quando o evento ocorre.
const Tab = (props) => {
const { className, label, isActive, onClick } = props;
const tabClass = `tabs__tab ${className}`;
const linkClass = isActive ? 'tabs__tab-link--active' : 'tabs__tab-link';
return (
<li className={tabClass}>
<a className={linkClass} onClick={onClick}>{label}</a>
</li>
);
};
Conclusão
Com apenas um pouco de código e ainda sim flexibilidade, criamos um componente <Tabs/>
simples, o qual nos permite evoluir-lo se necessário. O código fonte necessário para implementar os componentes podem ser encontrados neste gist, e o pequeno exemplo aqui.
Já implementou algo similar? Você faria algo diferente? Deixe um comentário e vamos aprender juntos :)
Join the Conversation