Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop function from being called on Render EVERY TIME

Tags:

In my React.JS app I have a function inside of a Functional Component that renders JSX that I need when the app loads initially. The catch is I don't need it re-run every time the component updates and the return() method is called. This content is rendered with an API call and it's "expensive" to re-run constantly.

How do I limit how often this will run.

...

<Col lg="8">
    <div className="tab-content" id="newForm-wrapper">
        {/* !! STOP THIS FROM BEING CALLED EVERY RENDER !!! */}
        {renderSectionContent(formJSON)}
    </div>
</Col>

...

Below is the function being called:

const renderSectionContent = json => {
    if (json.hasOwnProperty("form") && json.form.hasOwnProperty("sections")) {
        return json.form.sections.map((section, i) => {
            return <FormSection key={i} json={section} />;
        });
    } else {
        return <FormSection json={{}}/>;
    }
};
like image 528
DigitalMC Avatar asked Aug 09 '19 04:08

DigitalMC


2 Answers

To prevent this function from being run every render, you will need to store return value of the function in local state. You will probably want the function to be re-run every time the json data changes, which can be difficult if the json has deeply nested data. Depending on how you are injecting the json into your component will change exactly how you will set the state.

Here is an example of how this would be done assuming the data was passed as a prop to your class component:

export default class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      sectionContent: renderSectionContent(props.formJson),
    };
  }

  renderSectionContent = json => {
    // do stuff
    return <FormSection />;
  }

  render() {
    return (
      <Col lg="8">
        <div className="tab-content" id="newForm-wrapper">
          {sectionContent}
        </div>
      </Col>
    );
  }
}

Or using Hooks (new starting in React version 16.8):

export default function MyComponent(props) {
  const [sectionContent, setSectionContent] = useState(renderSectionContent(props.formJson));

  const renderSectionContent = json => {
    // do stuff
    return <FormSection />;
  }

  return (
    <Col lg="8">
      <div className="tab-content" id="newForm-wrapper">
        {sectionContent}
      </div>
    </Col>
  );
}
like image 188
Devin Cenatiempo Avatar answered Sep 25 '22 20:09

Devin Cenatiempo


Assuming your json input to your function is either being passed down as a prop to your component or is in state ( if it's not maybe you could store it in state? ) :

Two of some of the options you could use here are to either make that function into it's own component with returns the desired component based on the condition; this way you can pass down json as a prop and it will only run again if that prop changes. Or you could use createSelector from the react reselect library where you hold your json in state (or you can replace state with props in the example below ) and you pass in your state or prop to your function (which will now be a reselect function). This way your function will only run again if your json in state or props changes

Example

import { createSelector } from 'reselect'

static renderSectionContent = createSelector(
    (state) => state.json,
    (json) => {
        if (json.hasOwnProperty("form") && 
            json.form.hasOwnProperty("sections")) {
                return json.form.sections.map((section, i) => {
                    return <FormSection key={i} json={section} />;
                });
        } else {
            return <FormSection json={{}}/>;
        }
    }
)


render() {
    let selectionContent = this.renderSectionContent(this.state)


    ..

    <Col lg="8">
        <div className="tab-content" id="newForm-wrapper">
            {selectionContent}
        </div>
    </Col>

   ...
}

every time createSelector is called, it will return the previous computed return from the function, unless one of the inputs to the createSelector function change

like image 43
Jonathan Beadle Avatar answered Sep 26 '22 20:09

Jonathan Beadle