Search code examples
reactjshttp-redirectasync-awaitevent-handlingreact-props

How to await the completion of eventHandlers before redirect in React


I am writing a Messenger Application with Redux-React-Realtime databases. In a ProfileSearch page, there are many MemberCard. I need to write a particular feature where upon clicking the “message” button on a MemberCard while I’m technically on a profile page, I will redirect to the Messenger page, and with the channel_id that is passed to it via props, will load up all attributes related to the message_room.

However, I’m not sure how to make this interaction synchronized.

Currently, upon calling the message button, it redirects me to the Messenger component before my action in the MemberCard gets completely executed. Is there any particular way to await the completion of the eventHandler, get the return value inside some method of the event handler and pass that information to another component and redirecting there? The child component MemberCard does not have access to this.props.history unlike its parent ProfileSearch.

Here's a short snippet to give some context.

ProfileSearch.js

class ProfileSearch extends Component {



  constructor(props){
    super(props);
    this.state = {};
    const locals = this.state;
    locals[FILTERED_PROFILES] = [];
    //pass in searchUser
    locals["search"] = "";

  }



  renderProfiles = (profile) => {
    return (
      <MemberCard
        profile_id = {profile[PROFILE_ID]}
        display_name = {profile[PROFILE_PUBLIC_DISPLAY_NAME]}
      />)
  }


  onchange = e => {
    this.setState({ search: e.target.value });
  };

  render() {

    
    const { search } = this.state;
    const all_profiles = this.props[REDUX_DATA_ALL_PROFILES];
    const filtered_profiles = all_profiles.filter((profile) => {
      return profile[PROFILE_PUBLIC_DISPLAY_NAME].toLowerCase().indexOf(search.toLowerCase()) !== -1;
    });

    console.log("Printing filtered");
    console.log(filtered_profiles);

    return (
      <div className="flyout">
        <main style={{ marginTop: "4rem" }}>
          <div className="container">
            <div className="row">
              <div className="col">
                <Input
                  label="Search for profile display name"
                  icon="search"
                  onChange={this.onchange}
                />
              </div>
              <div className="col" />
            </div>
            <div className="profile-row">
              {filtered_profiles.map((profile) => {
                return this.renderProfiles(profile);
              })}
            </div>
          </div>
        </main>
      </div>
    );
  }
}

MemberCard.js

class MemberCard extends Component {
  //console.log(this.props) shows only user, profile_id and display_name
  handleSubmit = async (event) => {
    try {
      event.preventDefault();


      //some logic before calling the async function, which does firebase queries
      const channel_id = await select_or_create_message_room_method(channel);
      console.log(`Channel of uid ${channel_id} created`);
      //Passes channel_id to next component and redirect
    } catch (error) {
      console.log("Error with handleSubmit from MemberCard", error);
    }
  }



  render() {


    return (
      <Card className={"member"}>
        <CardActions>
          <Button size="small" color="secondary" onClick={this.handleSubmit} >
            <Link to={{
              pathname: APP_MESSENGER_PATH,
              state: {
                //Just testing out the features
                postalCode: 1,
                hasSubsidy: 2,
                subsidyType: 3,
                age: 4,
                nationality: 5
              }
            }}
            >
              <span style={{ color: "white" }}> Message { display_name } </span>
            </Link>
          </Button>
        </CardActions>
      </Card>
    );

  }
}

Messenger.js

class Messenger extends Component {


  //TODO: No need for PropTypes since it uses constructor pattern
  constructor(props) {

    super(props);
    this.state = {};
    const locals = this.state;
    locals[MESSENGER_ACTIVE_CHANNEL_ID] = this.props[MESSENGER_ACTIVE_CHANNEL_ID];//ideally gets the channel_id from the calling component
  }


  async componentDidMount() {

    console.log("Component did mount happen");


    if (this.state[MESSENGER_ACTIVE_CHANNEL_ID]) {
      //loads everything related to the active channel
    }


    console.log("Component did mount ended");
  }



  //TODO:most important, intercept here and change to Redux
  //console.log everything  here
  render() {
    //render items
  }
}

Update

I realised I left out some information which will affect the answer. It's an action that occurs in some child component, so the props.history may not be passed there.


Solution

  • Just remove Link and use history.push at the end of handleSubmit

    handleSubmit = async (event) => {
      try {
        event.preventDefault();
    
        //some logic before calling the async function, which does firebase queries
        const channel_id = await select_or_create_message_room_method(channel);
        console.log(`Channel of uid ${channel_id} created`);
        //Passes channel_id to next component and redirect
        this.props.history.push({
          pathname: APP_MESSENGER_PATH,
          state: {
            //Just testing out the features
            postalCode: 1,
            hasSubsidy: 2,
            subsidyType: 3,
            age: 4,
            nationality: 5,
          },
        });
      } catch (error) {
        console.log("Error with handleSubmit from MemberCard", error);
      }
    };
    
    

    To read data in Messenger component, do this:

    this.props.location.state
    

    Edit Based on comment...

    Your MemberCard component ideally should have access to history object as you are attempting to use Link over there.

    Anyway ... If you want to access history in MemberCard component, you have 2 options.

    1. Use withRouter for MemberCard component.
    2. Use custom history object and pass it to the Router. This way your MemberCard will have access to history object.