dragon
dragon

Reputation: 1274

How to avoid inline functions in React/Redux

I follow a React/Redux tutorial and from what I saw on a few articles on internet I realized that inline functions are bad for performance in React.

From what I understood functions are reference type and if you use an inline function, for every re-render, this function will take a different spot in memory.

In the tutorial example I have this deleteExperience() method, that the instructor used inline.

import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Moment from 'react-moment';
import { Link, withRouter } from 'react-router-dom';
import { deleteExperience } from '../../actions/profileActions';

const Experience = ({ experience, deleteExperience }) => {
  const experiences = experience.map(exp => (
    <tr key={exp._id}>
      <td>{exp.company}</td>
      <td className="hide-sm">{exp.title}</td>
      <td>
        <Moment format="YYYY/MM/DD">{exp.from}</Moment> -
        {exp.to === null ? (
          ' Now '
        ) : (
          <Moment format="YYYY/MM/DD">{exp.to}</Moment>
        )}
      </td>
      <td>
        <button className="btn btn-danger" onClick={() => deleteExperience(exp._id)}>
          Delete
        </button>
      </td>
    </tr>
  ));

  return (
    <Fragment>
      <h2 className="my-2">Experience Credentials</h2>
      <table className="table">
        <thead>
          <tr>
            <th>Company</th>
            <th className="hide-sm">Title</th>
            <th className="hide-sm">Years</th>
            <th />
          </tr>
        </thead>
        <tbody>{experiences}</tbody>
      </table>
    </Fragment>
  );
};

Experience.propTypes = {
  experience: PropTypes.array.isRequired,
  deleteExperience: PropTypes.func
};


export default connect(
  null,
  {deleteExperience}
)(withRouter(Experience));

So the instructor said that he used inline function

 onClick={() => deleteExperience(exp._id)}

and not just called directly the function

 onClick={deleteExperience(exp._id)}

to not be execute immediately.

So, please someone tell me, if the theory about bad practice to use inline function is true, how to handle this situation? I tried many ways, without any success.

Upvotes: 1

Views: 546

Answers (1)

The performance issue isn't from using arrow functions, but rather from creating fresh ones on every render. In your case, you can use useCallback() to memoize them. (You'll need to extract a component to render each exp object to avoid breaking the rules of hooks.) Something like this should work:

import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Moment from 'react-moment';
import { Link, withRouter } from 'react-router-dom';
import { deleteExperience } from '../../actions/profileActions';

const Exp = ({ exp, deleteExperience }) => {
  const del = useCallback(() => deleteExperience(exp._id), [deleteExperience, exp._id]);
  return (
    <tr>
      <td>{exp.company}</td>
      <td className="hide-sm">{exp.title}</td>
      <td>
        <Moment format="YYYY/MM/DD">{exp.from}</Moment> -
        {exp.to === null ? (
          ' Now '
        ) : (
          <Moment format="YYYY/MM/DD">{exp.to}</Moment>
        )}
      </td>
      <td>
        <button className="btn btn-danger" onClick={del}>
          Delete
        </button>
      </td>
    </tr>
  );
};

const Experience = ({ experience, deleteExperience }) => {
  const experiences = experience.map(exp => (
    <Exp key={exp._id} exp={exp} deleteExperience={deleteExperience} />
  ));

  return (
    <Fragment>
      <h2 className="my-2">Experience Credentials</h2>
      <table className="table">
        <thead>
          <tr>
            <th>Company</th>
            <th className="hide-sm">Title</th>
            <th className="hide-sm">Years</th>
            <th />
          </tr>
        </thead>
        <tbody>{experiences}</tbody>
      </table>
    </Fragment>
  );
};

Experience.propTypes = {
  experience: PropTypes.array.isRequired,
  deleteExperience: PropTypes.func
};


export default connect(
  null,
  {deleteExperience}
)(withRouter(Experience));

Upvotes: 5

Related Questions