OvidijusParsiunas / deep-chat

Fully customizable AI chatbot component for your website
https://deepchat.dev
MIT License
1.27k stars 175 forks source link

deep-chat re rendering when other inputs are updated #122

Closed rajeshwarpatlolla closed 4 months ago

rajeshwarpatlolla commented 4 months ago

Environment: Technology Stack: Vue 3 Supported Browsers: All web browsers

Issue Description: On a page containing a deep-chat component with additional configuration, modifying input elements with the attached v-model causes the deep-chat component to re-render, resulting in the loss of all messages. However, removing the v-model from the inputs resolves this issue, allowing input field updates without affecting the deep-chat component.

Steps to Reproduce:

Expected Behavior: The deep-chat component should maintain its functionality without being affected by updates to other input fields with v-models.

Please provide instructions on how to prevent the deep-chat component from re-rendering even when other elements in the DOM are updated.

Code


<template>
  <h1>Deep Chat</h1>
  <!-- demo/textInput are examples of passing an object directly into a property (if this does not work refer to data values like in the initialMessages example) -->
  <!-- initialMessages is an example of passing a data object into a property -->
  <input v-model="name" />
  <deep-chat
    key="deep-chat-key"
    class="deep_chat"
    id="deep-chat-element"
    :textInput="{
      styles: {
        container: { padding: '6px', width: '100%' },
      },
      placeholder: { text: 'Ask value copilot..' },
    }"
    :messageStyles="{
      default: {
        shared: {
          bubble: {
            maxWidth: '100%',
            backgroundColor: 'unset',
            marginTop: '10px',
            marginBottom: '10px',
          },
        },
        user: { bubble: { marginLeft: '0px', color: 'black' } },
        // eslint-disable-next-line vue/no-parsing-error, vue/no-parsing-error
        ai: {
          innerContainer: { borderRadius: '15px', backgroundColor: 'white' },
        },
      },
    }"
    :submitButtonStyles="{
      submit: {
        container: {
          default: { width: '34px', height: '34px' },
        },
      },
    }"
  />
</template>
OvidijusParsiunas commented 4 months ago

Hi @rajeshwarpatlolla. v-model in Vue is used to achieve reactivity - hence it will force a complete re-render of the component's template. If Deep Chat is within the same component/template - it will inherently also be re-rendered. There is no real way to prevent this in Deep Chat as this is simply how Vue and other frameworks handle reactive state. I know that you mentioned the removal of textInput, messageStyles, user, and submitButtonStyles no longer forces a re-render - but this does not always seem to be the case when I tested it - and is likely something weird based on how Vue decides to re-render elements/state.

The recommend approach is to track new chat messages via the onNewMessage event, store them somewhere like localStorage and then re-populate them using initialMessages.

Here is a quick example for Vue:

<template>
  <p>Message is: {{ message }}</p>
    <input v-model="message" placeholder="This will cause a re-render" />
  <deep-chat
    :demo="true"
    :onNewMessage="(message) => {
      if (!message.detail.isInitial) {
        initialMessages.push(message.detail.message);
      }
    }"
    :initialMessages="initialMessages"
  />
</template>

<script setup>
  import "deep-chat"
  import { ref } from 'vue'
  const initialMessages = [];
  const message = ref('')
</script>

Let me know if this helps. Thankyou!

rajeshwarpatlolla commented 4 months ago

Thanks for the response @OvidijusParsiunas. If consistency in reactivity is the concern, shouldn't it consistently behave the same way? Currently, it only functions properly when the additional configuration options such as textInput, messageStyles, user, and submitButtonStyles are removed. However, it fails to function properly if any of these options are added.

You can observe this behavior in the example provided here. Try entering text in the deep-chat text box first, and then enter something in the text box at the top of the page. You'll notice that the chat resets. Removing all additional configurations resolves the issue and restores normal functionality.

OvidijusParsiunas commented 4 months ago

I see what the problem is, when you update the v-model state, the properties that you are binding to Deep Chat are reapplied again - which in turn causes Deep Chat to re-render. When you don't bind anything, there is nothing to be reapplied - hence the component is not re-rendered. This is evident because binding demo/request/directConnection properties does not re-render Deep Chat as those properties are set up to not re-render the component when reapplied.

Overall, the re-render from Deep Chat is expected behaviour when new properties are applied/reapplied and it is Vue as a framework that is making this problematic. Having said that, DOM re-rendering on reactive state change is a common practice across most popular frameworks and Vue is no exception, so when state changes you can always be certain that a part of a component or the entire component will be re-rendered. When it comes to Vue, I noticed that binding properties to the data state can actually help prevent the properties from being reapplied: Changing this:

<deep-chat :introMessage="{text: 'Send a chat message to an example server.'}" />

To this:

<deep-chat :introMessage="introMessage" />

...

export default {
  ...
  data() {
    return {
      introMessage: { text: 'Send a chat message to an example server.' },
    };
  },
};

There are plenty of other tutorials that also suggest the use of key/v-memo etc, but they don't always seem to work or at least when I tried them. You can also try to use a state management system like Vuex or Pinia that might help prevent unwanted DOM re-renders.

Another attempt that you can use is to place the v-model or any other state that can change into their own separate component which will prevent the other sibling elements from being re-rendered.

The bottom line is that Deep Chat is behaving as required and the change will need to be made in your Vue app - using one of the suggestions above or other Vue specific config. You can ofcourse always fork/clone Deep Chat, and set it up on your end - which is pretty easy to do if you follow these instructions and try to make changes in the renderControl.ts file which may be sufficient for your particular case.

OvidijusParsiunas commented 4 months ago

i will be closing this issue since the original problem has been resolved. Feel free to comment below or create a new issue for anything else. Thank you!