Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

reactjs

redux

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>
    );
  }
}
like image 797
empyreal Avatar asked Oct 30 '22 12:10

empyreal


1 Answers

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);
like image 157
Radio- Avatar answered Nov 09 '22 05:11

Radio-