Make sure function is called only once, either in componentDidMount or componentDidUpdate

Viewed 4210

I would like to make sure that a function is called exactly one time, depending on some props and state.

class MyComponent extends Component {
  state = {
    externalInfoPresent: false,
    infoSaved: false,
  }

  async componentDidMount() {
    await this.props.refreshExternalInfo();
    this.setState({ externalInfoPresent: true });
    if (this.props.externalInfo !== undefined && !this.state.infoSaved) {
      await this.saveMyInfo();
    }
  }

  async componentDidUpdate(prevProps) {
    if (prevProps.externalInfo === this.props.externalInfo || this.state.infoSaved) return;
    await this.saveMyInfo();
  }

  async saveMyInfo() {
    if (this.props.externalInfo === undefined || this.state.infoSaved) return;
    // logic for saving stuff to external service
    this.setState({ infoSaved });
  }

  // render and other stuff
}

saveMyInfo() depends on externalInfo being present.

I would like saveMyInfo() to only be called once but it's being called twice with my current logic.

3 Answers

If you want it to be called only once, then componentDidMount is the place (it gets called exactly once in the lifecycle). Therefore you don't need it in ComponentDidUpdate (which gets called everytime a prop changes (after the initial render)!

Lifecycle: https://hackernoon.com/reactjs-component-lifecycle-methods-a-deep-dive-38275d9d13c0

Updated..

I think you Just need to set infosaved to true (this.setstate({infosaved:true}) after you call savemyinfo in componentdidupdate.

Add an additional flag:

class MyComponent extends Component {
  state = {
    externalInfoPresent: false,
    infoSaved: false,
    saveMyInfoCalled: false,
  }

  async componentDidMount() {
    await this.props.refreshExternalInfo();
    this.setState({ externalInfoPresent: true });
    if (this.props.externalInfo !== undefined && !this.state.infoSaved) {
      await this.saveMyInfo();
    }
  }

  async componentDidUpdate(prevProps) {
    if (this.props.externalInfo !== undefined && !this.state.saveMyInfoCalled && !this.state.infoSaved) {
      await this.saveMyInfo();
    }
  }

  async saveMyInfo() {
    this.setState({saveMyInfoCalled: true});
    if (this.props.externalInfo === undefined || this.state.infoSaved) return;
    // logic for saving stuff to external service
  }

  // render and other stuff
}

You probably should call this.setState({saveMyInfoCalled: false}); somewhere if external service fails. So saveMyInfo doesn't get called several times simultaneously but has a second chance if something goes wrong.

class MyComponent extends Component {
  async componentDidMount() {
    await this.props.refreshExternalInfo();
    await this.saveMyInfo();
  }

  async componentDidUpdate() {
    await this.saveMyInfo();
  }

  async saveMyInfo() {
    // if we don't have the info then skip for now
    if (this.props.externalInfo === undefined || this.infoSaved) return;

    // keep track that we already saved
    this.infoSaved = true;

    // perform save
  }
}
Related