React: Component Helper Functions

Intro

One of the well known conventions of React components is that any logic should be broken out of the render function into a helper function. This keeps the component clean, and separates the concerns inside of the component.

I find myself constantly forgetting the correct syntax for the helper functions, especially since it is different for class vs functional components. So once again, here we are, taking notes.

Examples

Class Component: Render Icon

Here is a simple example where we want to render a different icon depending on the state of a menu (collapsed vs expanded). We can see that the logic for deciding which icon gets rendered is broken out into a helper function ABOVE the render function, called renderCollapsedIcon(). Then we simply call that function inside the render function, and the correct icon is dynamically inserted depending on the state of the menu.

components/MenuCollapseButton.js
import React, { Component } from "react"
import { connect } from "react-redux"

/* Redux Actions */
import { toggleMenu } from "../actions/index"

/* Icons */
import ChevronLeft from "./icons/ChevronLeft"
import ChevronRight from "./icons/ChevronRight"

class ContentMenuCollapse extends Component {
  /* change icon on menu toggle */
  renderCollapseIcon() {    /* full menu */    if (this.props.collapse === false) {      return <ChevronLeft Color={this.props.Color} />    }    /* collapsed menu */    return <ChevronRight Color={this.props.Color} />  }
  render() {
    return (
      <div
        className="content-menu-collapse"
        onClick={() => this.props.toggleMenu(this.props.collapse)}
      >
        {this.renderCollapseIcon()}      </div>
    )
  }
}

const mapStateToProps = state => {
  return { collapse: state.menuCollapse }
}

export default connect(mapStateToProps, { toggleMenu })(ContentMenuCollapse)

This example is using Redux, if you are wondering what all the extra code is for here. In addition we can actually simplify this render function more by pulling out the click event handler into a helper function like so.

components/MenuCollapseButton.js
import React, { Component } from "react";
import { connect } from "react-redux";

/* Redux Actions */
import { toggleMenu } from "../actions/index";

/* Icons */
import ChevronLeft from "./icons/ChevronLeft";
import ChevronRight from "./icons/ChevronRight";

class ContentMenuCollapse extends Component {
  /* Helper Functions */

  /* click handler */
  onCollapseClick() {    /* toggle menu redux action */    return () => this.props.toggleMenu(this.props.collapse);  }
  /* change icon on menu toggle */
  renderCollapseIcon() {
    /* full menu */
    if (this.props.collapse === false) {
      return <ChevronLeft Color={this.props.Color} />;
    }
    /* collapsed menu */
    return <ChevronRight Color={this.props.Color} />;
  }

  /* Render */
  render() {
    return (
      <div
        className="content-menu-collapse"
        onClick={this.onCollapseClick()}      >
        {this.renderCollapseIcon()}
      </div>
    );
  }
}

const mapStateToProps = (state) => {
  return { collapse: state.menuCollapse };
};

export default connect(mapStateToProps, { toggleMenu })(ContentMenuCollapse);

This may seem unnecessary but with this organization the click event handler can grow in complexity without muddling up the render function.

Functional Component: Looped/Mapped Content

The syntax is different for functional components, so here is a functional example.

Another time that this method is useful is if we want to display a variable array or object of items. In this example we have an accordion component that is receiving an object full of text content with a title and a paragraph. We need to loop over the content and create a set of divs for each item and then insert that set of content into the render function. Although since this is a functional component it simply goes into the return statement.

components/Accordion.js
import React from "react"

const Accordion = ({ items }) => {
  /* Helper Functions */
  const renderedItems = items.map((item, index) => {    return (
      <React.Fragment key={item.title}>
        <div
          className="title active"
          onClick={() => console.log("Title clicked", index)}
        >
          <i className="dropdown icon"></i>
          {item.title}
        </div>
        <div className="content active">
          <p>{item.content}</p>
        </div>
      </React.Fragment>
    )
  })

  /* Render */
  return <div className="ui styled accordion">{renderedItems}</div>}

export default Accordion

and this component should actually be broken out further by pulling the onClick event handler (which is logic) out of the render area as well. In this case the event handler is very simple, but in other cases it may not be!

components/Accordion.js
import React from "react"

const Accordion = ({ items }) => {
  /* Helper Functions */
  const onTitleClick = index => {    console.log("Title clicked", index)  }  const renderedItems = items.map((item, index) => {
    return (
      <React.Fragment key={item.title}>
        <div className="title active" onClick={() => onTitleClick(index)}>          {" "}
          <i className="dropdown icon"></i>
          {item.title}
        </div>
        <div className="content active">
          <p>{item.content}</p>
        </div>
      </React.Fragment>
    )
  })

  /* Render */
  return <div className="ui styled accordion">{renderedItems}</div>
}

export default Accordion

Create as many helper functions as you need to keep the render area clean. I promise this pattern will make your components easier to create and maintain as they gain complexity.