UI won't update even when render() function is being triggered in react

28 views Asked by At

Im a beginner at react. I'm using the gpt3.5 model and eventSource to try to mimic the phrase by phrase generation that chat gpt has. To do this whenever on message is triggered, I change the state by using setState to append the new words to the current message. I read that react uses batching to optimize performance but I want the UI to display the changes after every word sent by eventSource so I have used both flushSync and forceUpdate() in an attempt to get it to re-render. The issue I'm having is that even though the render() function is being called (the console.log() I have in the render() triggers every time setState changes the state), it isn't actually reflected in the UI of the web page. Only after the eventSource connection is closed and the entire message is generated is the message displayed. I'm not sure why the UI won't update.

Here's the code for the component:

class Chat extends Component {
  constructor(props) {
    super(props);

    this.state = {
      loading: true,
      reply: null,
    };
  }
  componentDidMount() {
    const chat_id = this.props.chat_id;
    const question = this.props.steps.user_ask.value;
    const current_turn = { turn_type: "user", turn_message: question };
    this.props.addChatStep(current_turn);
    axios
      .get(
        `/api/chat/get_reply?chat_id=${chat_id}&current_turn=${JSON.stringify(
          current_turn
        )}`
      )
      .then((res) => {
        const chat_id = res.data["chat_id"];
        const should_trigger_register = res.data["should_trigger_register"]
        const should_trigger_connect_lawyer = res.data["should_trigger_connect_lawyer"]
        const should_trigger_botreply_options = res.data["should_trigger_botreply_options"]
        const is_first_reply = res.data["is_first_reply"]
        const is_scripted_reply = res.data["is_scripted_reply"]
        if (is_scripted_reply) {
          const reply = res.data["reply"];
          this.setState({ loading: false, reply: reply });
          this.props.addChatStep(reply);
          this.props.setChatId(chat_id);
          // track
          if (is_first_reply) {
            mixpanel.track('Event: Server Replied First Time');
          }
          // mixpanel.track('Event: Server replied');

          if (should_trigger_register) {
            this.props.triggerNextStep({ trigger: "register_name" });
            // track
          } else if (should_trigger_connect_lawyer) {
            this.props.triggerNextStep({ trigger: "connect_lawyer" });

          } else if (should_trigger_botreply_options) {
            this.props.triggerNextStep({ trigger: "bot_reply_options" });
            // track
            // mixpanel.track('reply options triggered');
          } else {
            this.props.triggerNextStep({ trigger: "user_ask" });
          }
        }
        else {
          console.log("entering eventSource")
          const source = new EventSource(`/stream-chat?chat_id=${chat_id}&current_turn=${JSON.stringify(current_turn)}`);
          console.log(source)
          var new_reply = {turn_type: "bot", turn_message: ""}
          this.setState({loading : false, reply : new_reply})
          source.onerror = (e) => {
            source.close();
            this.props.addChatStep(this.state.reply);
            this.props.setChatId(chat_id);
            console.log("connection CLOSED YAY")
            // track
            if (is_first_reply) {
              mixpanel.track('Event: Server Replied First Time');
            }
            // mixpanel.track('Event: Server replied');
  
            if (should_trigger_register) {
              this.props.triggerNextStep({ trigger: "register_name" });
              // track
            } else if (should_trigger_connect_lawyer) {
              this.props.triggerNextStep({ trigger: "connect_lawyer" });
  
            } else if (should_trigger_botreply_options) {
              this.props.triggerNextStep({ trigger: "bot_reply_options" });
              // track
              // mixpanel.track('reply options triggered');
            } else {
              this.props.triggerNextStep({ trigger: "user_ask" });
            }
            axios.get(
              `/api/chat/add_message_after_generation?chat_id=${chat_id}&current_turn=${JSON.stringify(
                current_turn
              )}&reply_msg=${this.state.reply["turn_message"]}`
            )
            .then((res) => {
              console.log("adding message SUCCESS!")
            }).catch((err) => {
              console.log("error in adding message")
            });
          };
          source.onopen = function() {
            console.log('Connection opened');
          };
          source.onmessage = (event) => {
      
            flushSync(() => {
              this.setState((prevState) => ({
              reply: {
                ...prevState.reply,
                turn_message: prevState.reply.turn_message + event.data
              }
            }), () => console.log(this.state.reply.turn_message));
          } )
      this.forceUpdate()
        }
        }
        this.props.setChatId(chat_id);
      })
      .catch((err) => {
        const turn_data = { turn_type: "bot", turn_message: "An error happened. Please try again." };
        this.setState({ loading: false, reply: turn_data });
        this.props.triggerNextStep({ trigger: "user_ask" });
        console.log(err);
        // track
        mixpanel.track('Error: server error');
      });
  }

  render() {
    const { loading, reply } = this.state;
    if (reply != null) {
      console.log("reply triggered: " + reply.turn_message)
    }
    else {
      console.log("very first render")
    }
    return (
      <div>
        {loading ? (
          <Loading />
        ) : (
          <ReactMarkdown className="prose" children={reply.turn_message} />
          // <div> {reply.turn_message} </div>
        )
        }
      </div>
    );
  }
}

I've tried using forceUpdate() and flushSync but the UI won't update.

0

There are 0 answers