reactjsreact-lifecyclereact-lifecycle-hooks

ReactJs: How to execute a function that depends on the result of a setState that's called inside componentDidUpdate


I have this class that loads messages for different conversations:

export class Messages extends Component {
  constructor() {
    super();
    this.state = {
      page: 0,
      results_per_page: 10,
      messages: [],
    };

    this.loadAnotherSetOfMessages = this.loadAnotherSetOfMessages.bind(this);
    this.checkIfUserHasPickedAnotherConversation =
      this.checkIfUserHasPickedAnotherConversation.bind(this);
  }

  componentDidMount() {
    // #NOTE
    // This will load first set of messages for first conversation opened by user
    this.loadAnotherSetOfMessages();
  }
  componentDidUpdate(prevProps, prevState) {
    this.checkIfUserHasPickedAnotherConversation(prevProps);
  }
  loadAnotherSetOfMessages() {
    const { page, results_per_page } = this.state;
    const conv_id = this.props.private_chat.current_chat._id;
    this.props.getXNumberOfMessages(conv_id, page, results_per_page);
    this.setState({
      page: this.state.page + 1,
    });
  }
  checkIfUserHasPickedAnotherConversation(prevProps) {
    const has_user_clicked_a_conversation =
      !!this.props.private_chat.current_chat;

    if (has_user_clicked_a_conversation) {
      const previous_conv_id = prevProps.private_chat.current_chat._id;

      const new_conv_id = this.props.private_chat.current_chat._id;

      const has_user_picked_another_conversation =
        previous_conv_id != new_conv_id;

      if (has_user_picked_another_conversation) {
        this.resetMessages();
        this.loadAnotherSetOfMessages();
      }
    }
  }

  resetMessages() {
    this.setState({
      page: 0,
      messages: [],
    });
  }
  // render() {}
}

Everytime a user clicks on a new conversation, I reset the state using this function resetMessages:

  if (has_user_picked_another_conversation) {
    this.resetMessages();
    // This function needs to get executed after the update inside resetMessages has happened
    this.loadAnotherSetOfMessages();
  }

And this is its definition:

  resetMessages() {
    this.setState({
      page: 0,
      messages: [],
    });
  }

So that when the function that gets executed after it, loadAnotherSetOfMessages which is defined like this:

  loadAnotherSetOfMessages() {
    const { page, results_per_page } = this.state;
    const conv_id = this.props.private_chat.current_chat._id;
    this.props.getXNumberOfMessages(conv_id, page, results_per_page);
    this.setState({
      page: this.state.page + 1,
    });
  }

Uses the initial state params page with value 0 and messages with value [] since it's a whole new conversation.

The Problem that I am facing is that the function loadAnotherSetOfMessages gets executed not with the reinitialized state params of 0 and [] but with the most recent values of the previous conversation.
For example, if the previous conversation has been loaded until page 5 then it will use 5 instead of 0.
What should I do?


Solution

  • You cannot do using the state, the reason being react's setState is asynchronous, which means after you set a new value in state, you will have to wait for the next re-render of the component to fetch the updated state value. If you try to fetch the state value immediately after setting it, you will always end up with the previous value.

    So the best thing to do here is that, you pass the new state values are arguments to your loadAnotherSetOfMessages funtion and use that. Like

    loadAnotherSetOfMessages(page, results_per_pag) {
        const conv_id = this.props.private_chat.current_chat._id;
        this.props.getXNumberOfMessages(conv_id, page, results_per_page);
        this.setState({
          page: page + 1,
        });
    }
    

    And call it like

    this.loadAnotherSetOfMessages(page, results)