JS/React – Why is my array rendering in the wrong order?

Currently, I am making a private messaging feature for a React app. I’m fairly new to React, so I’m not sure where I’m messing this up. Also, please note: For this particular project, I am unable to use jQuery/Redux/etc… just plain React.

So before I show my code, this is what results I have now:

enter image description here

The sent messages appear ordered properly, and the received messages are ordered properly, BUT it shows all sent messages before then showing all received messages. I would like for them all to display in the order that they were sent, regardless of which user sent it (think of how your average texting app works)

Now for my code (sorry I included a lot, I’m not sure where in the process it is messing up):

data.json

"privateMessages": [
    {
      "userId": 2,
      "receiverId": 3,
      "message": "first",
      "id": 1
    },
    {
      "userId": 3,
      "receiverId": 2,
      "message": "second",
      "id": 2
    },
    {
      "userId": 2,
      "receiverId": 3,
      "message": "third",
      "id": 3
    },
    {
      "userId": 2,
      "receiverId": 3,
      "message": "fourth",
      "id": 4
    },
    {
      "userId": 3,
      "receiverId": 2,
      "message": "fifth",
      "id": 5
    }
  ],
"users": [
    {
      "name": "User 1",
      "id": 2
    },
    {
      "name": "User 2",
      "id": 3
    }

MsgCard.js — I suspect the problem is here but not sure.

export const MsgCard = ({message}) => {
    const currentUserId = sessionStorage.getItem("nutshell_user");
    if (message.sentBySelf) {
        return (
            <div className="sentMsg">
                <h4>
                    <span className="nameOfSender">{message.user?.name}: </span>
                    {`${message.message}`}
                </h4>
            </div>
        )
    } else if (message.receiverId === parseInt(currentUserId)) {
        return (
            <div className="receivedMsg">
                <h4>
                    <span className="nameOfSender">{message.user?.name}: </span>
                    {`${message.message}`}
                </h4>
            </div>
        )
    }
}

MsgList.js

export const MsgList = () => {
    const [messages, setMessages] = useState([])
    const currentUserId = sessionStorage.getItem("nutshell_user");
    
    const getAllPMs = () => {
    return fetch(`${remoteURL}/privateMessages/?_expand=user`)
        .then(res=>res.json())
    }

    const scrollToEnd = () => {
        const container = document.querySelector(".messagesContainer")
        container.scrollTop = container.scrollHeight
    }

    const getPMs = () => {
        
        getAllPMs().then(allPMs => {
            const sentByCurrent = allPMs.filter(message => message.userId === parseInt(currentUserId))
            const sentByOthers = allPMs.filter(message => message.userId !== parseInt(currentUserId))

            const newCurrent = sentByCurrent.map(message => { 
                message.sentBySelf = true
                return message
            })
            const newOthers = sentByOthers.map(message => {
                message.sentBySelf = false
                return message
            })

            const allMessages = newCurrent.concat(newOthers)
            return allMessages
        })
        .then(allMsgsArray => {
            setMessages(allMsgsArray)
        })
        .then(() => {
            scrollToEnd()
        })
    }

    useEffect(() => {
        getPMs()
    },[])
    
    if (messages.length > 0) {
        return (
            <>
            <div className="messagesContainer">
                {messages.map(message => {
                    return <MsgCard key={message.id} message={message} />
                })}
            </div>
            IGNORE THIS COMPONENT<MsgInput renderList={getPMs} />
            </>
        )
    } else {
        return (
            <>
            <div className="messagesContainer">

            </div>
            IGNORE THIS COMPONENT <MsgInput renderList={getPMs} />
            </>
        )
    }
}

Answer

Your issue is how you handle adding the sentBySelf attribute to each message:

getAllPMs().then(allPMs => {
    const sentByCurrent = allPMs.filter(message => message.userId === parseInt(currentUserId))
    const sentByOthers = allPMs.filter(message => message.userId !== parseInt(currentUserId))

    const newCurrent = sentByCurrent.map(message => {
        message.sentBySelf = true
        return message
    })
    const newOthers = sentByOthers.map(message => {
        message.sentBySelf = false
        return message
    })

    const allMessages = newCurrent.concat(newOthers)
    return allMessages
})

You filter all sentByCurrent then all sentByOthers then when you rebuild the list you concat current with others. Ensuring all current come before all others.


You can use map instead to add the attribute without affecting the order.

const allPMs = [
    {
        "userId": 2,
        "receiverId": 3,
        "message": "first",
        "id": 1
    },
    {
        "userId": 3,
        "receiverId": 2,
        "message": "second",
        "id": 2
    },
    {
        "userId": 2,
        "receiverId": 3,
        "message": "third",
        "id": 3
    },
    {
        "userId": 2,
        "receiverId": 3,
        "message": "fourth",
        "id": 4
    },
    {
        "userId": 3,
        "receiverId": 2,
        "message": "fifth",
        "id": 5
    }
];

const currentUserId = "2";


const orderMaintained = allPMs.map(curr => ({...curr, sentBySelf: curr.userId === parseInt(currentUserId)}));

console.log(orderMaintained);

In your program this would look something like:

getAllPMs().then(allPMs => {
    return allPMs.map(curr => ({...curr, sentBySelf: curr.userId === parseInt(currentUserId)}));
})