chat-ui-kit-react icon indicating copy to clipboard operation
chat-ui-kit-react copied to clipboard

Exception occurs inside <MessageList> when loop using map

Open saqygee opened this issue 1 year ago • 8 comments

Hi Devs, I am trying to loop through the components there is an exception which occurs when i try to wrap <MessageSeparator/> and <Message/> elements inside <></> it means MessageList doesn't handle empty elements or divs.

const exampleObject = {"2023-03-06":["a","b","c"]};
 <MessageList>
              {
                Object.keys(exampleObject).map(
                  (timestampKey) => (
                      <> // here is the problem
                      <MessageSeparator content={timestampKey} key={uuid()} />
                      {exampleObject[timestampKey].map((values) => (
                        <Message
                          key={uuid()}
                          model={{
                            message: values.message,
                            sentTime: values.sentTime,
                            sender: values.sender,
                            direction: values.direction,
                            position: values.position,
                          }}
                        >
                          <Avatar src={avatarIco} name={values.sender} />
                        </Message>
                      ))
                    }
                    </>
                  )
                )
              }
            </MessageList>

saqygee avatar Mar 06 '23 15:03 saqygee

@saqygee This isn't a Chatscope issue. You're using array maps with React elements incorrectly. You need to use React.Fragment and add a unique key attribute.

https://reactjs.org/docs/fragments.html#keyed-fragments

j-krl avatar Mar 06 '23 20:03 j-krl

I already tried that you get this exception : react_devtools_backend.js:2655 Warning: Failed prop type: "undefined" is not a valid child for ForwardRef(MessageListFunc). Allowed types: Message, MessageGroup, MessageSeparator, MessageListContent It says undefined because it expects tags one of those allowed types

saqygee avatar Mar 06 '23 23:03 saqygee

Right. That's because it says in the docs the only allowed components inside a MessageList are one of those components, so it's by design. I agree that it should be able to accept Fragments though.

One workaround is to make an array of your elements using reduce and then put the array directly into the MessageList. I haven't tested it, but something like this:

const elementArray = Object.keys(exampleObject).reduce((accumulator, timestampKey) => (
  accumulator.concat([
    <MessageSeparator content={timestampKey} key={uuid()} />,
    <MessageGroup key={uuid()}>
      <Avatar src={avatarIco} name={values.sender} />
      <MessageGroup.Messages>
        {exampleObject[timestampKey].map((values) => (
          <Message
            key={uuid()}
            model={{
              message: values.message,
              sentTime: values.sentTime,
              sender: values.sender,
              direction: values.direction,
            }}
          />
        ))}
      </MessageGroup.Messages>
    </MessageGroup>
  ])
), [])

Then setup your MessageList like this:

<MessageList>
  {elementArray}
</MessageList>

Using a forEach would work as well I'm sure.

j-krl avatar Mar 07 '23 00:03 j-krl

Is there any other workaround ?

jimmykane avatar Apr 22 '23 17:04 jimmykane

Maybe a better alternative is to return an array inside of the .map.

<MessageList>
  {
                Object.keys(exampleObject).map(
                  (timestampKey) => [
                      <MessageSeparator content={timestampKey} key={uuid()} />,
                      exampleObject[timestampKey].map((values) => (
                        <Message
...
                        >
...                        </Message>
                      ))
                    </>
                  )
                )
  }
</MessageList>

mboettcher avatar Jun 13 '23 11:06 mboettcher

@saqygee This isn't a Chatscope issue. You're using array maps with React elements incorrectly. You need to use React.Fragment and add a unique key attribute.

https://reactjs.org/docs/fragments.html#keyed-fragments

Just to avoid confusions the empty tag is a React.Fragment, see https://react.dev/reference/react/Fragment

This is a chatscope issue as an error is thrown if React.Fragment is used. But, for reference, you can add other elements like Box to MessageList and while it will throw that error it will render them (although not 100% sure that the scrolling is perfect, but for debugging information it works), so the restriction on child nodes should be explained better (what types of nodes are allowed as children, in what conditions and what will not work if not using only the recommended children)

Edit: clarified that some other elements can be rendered, irrespective of the error.

vladmihaisima avatar Jan 15 '24 10:01 vladmihaisima

It's pretty clear in the docs the allowed child components are:

<Message />
<MessageGroup />
<MessageSeparator />
<MessageListContent />

j-krl avatar Jan 15 '24 14:01 j-krl

And what does it mean "not allowed"? If I put a Box it will be rendered. By reading the docs I assumed it will NOT be rendered (or that everything will break), but I did not clean up older code and to my surprise the Box got rendered and it is perfectly fine for my use case (which again, was to show some additional information in admin mode, nothing I could not do otherwise, but much easier if it just works).

Sure, the library can make another release and "ban/block" this behavior, but it would be nicer to just explain "using other elements than these list will result in X".

vladmihaisima avatar Jan 15 '24 14:01 vladmihaisima