0

I have a complex array filled the objects .(Shown below)

 const privateMessages = Array [
  Object {
    "_id": "607533d511a2301b204720ed",
    "chat": Array [
      Object {
        "_id": "6098ffd30a2f4287f92be018",
        "createdAt": "2021-05-10T09:41:39.683Z",
        "text": "G",
        "user": Object {
          "_id": "60716a38136f970ba4a0526e",
        },
      },
      Object {
        "_id": "6097b6e69f98cbf5ba6deaf8",
        "createdAt": "2021-05-09T10:18:36.972Z",
        "text": "ত",
        "user": Object {
          "_id": "60716f7cf3a75846ee1f5d38",
        },
      },
      Object {
        "_id": "6097b6aacd62683ba317965d",
        "createdAt": "2021-05-09T10:17:36.182Z",
        "text": "H",
        "user": Object {
          "_id": "60716a38136f970ba4a0526e",
        },
      }
    ],
    "pair": Array [
      Object {
        "_id": "60716a38136f970ba4a0526e",
        "firstName": "Sayan",
        "lastName": "Biswas",
      },
    ],
  },
]

It is quite hard to update it using setState hook , so I decided to use force Update to re-render the component but its not working. The way I am updating the state is given below

privateMessages.forEach((i) => {
                    if (i.pair[0]._id == data.sender) {
                        i.chat.unshift(data.chatObj)
                    }
                })

This is the forceUpdate hook I am using

function useForceUpdate() {
    const [value, setValue] = useState(0); // integer state
    return () => setValue(value => value + 1); // update the state to force render
}

The way I am calling it

const forceUpdate = useForceUpdate()

I am using the same forceUpdate() hook in the other part of my project and its working properly but I dont know why is it not working with the nested map function given below

Edit :-

Many of the answers told me not to use forceUpdate so I removed them . I tried all the solutions given in the answer but none of them is working.Getting idea from the answer of @Punisher , I made a solution myself of the state update part but the app crashes when it runs.

socket.on("recieve-private-message", (data) => {
                setPrivateMessages(prevState => {
                    const privateMessagesCopy = JSON.parse(JSON.stringify(prevState))
                    privateMessagesCopy.forEach((i) => {
                        if (i.pair[0]._id == data.sender) {
                            i.chat.unshift(data.chatObj)
                        }
                    })
                    return privateMessagesCopy
                }                    
                )
            })

The whole code :-

import React, { useState, useEffect, useContext } from 'react';
import { View, Text, TouchableOpacity, ScrollView } from 'react-native';
import AsyncStorage from "@react-native-async-storage/async-storage"
import API from '../api.js'
import { useNavigation } from '@react-navigation/native';
import { SocketObj } from "./Main"

function useForceUpdate() {
    const [value, setValue] = useState(0); // integer state
    return () => setValue(value => value + 1); // update the state to force render
}

const ChatScreen = () => {

    const navigation = useNavigation()

    let socket = useContext(SocketObj)

    let [privateMessages, setPrivateMessages] = useState([])

    let [userId, setUserId] = useState('')

    const forceUpdate = useForceUpdate()

    useEffect(
        () => {
            const fetchData = async () => {
                try {
                    const response = await API.get('get/chats', {
                        headers: {
                            'Content-Type': 'application/json',
                            "auth-token": await AsyncStorage.getItem("token")
                        }
                    })
                    setPrivateMessages(response.data.chatFriends)
                    setUserId(response.data.userId)
                } catch (err) {
                    console.log(err.response)
                }
            }
            fetchData()

            socket.on("recieve-private-message", (data) => {
                privateMessages.forEach((i) => {
                    if (i.pair[0]._id == data.sender) {
                        i.chat.unshift(data.chatObj)
                    }
                })
                forceUpdate()
            })
        }, []
    )


    useEffect(
        () => {
            console.log("changed")
        },[privateMessages]
    )

    return (<View>
        <ScrollView >
            {
                privateMessages.map(
                    (i) => i.pair.map(
                        (j) => {
                            return (
                                <>
                                    <TouchableOpacity
                                        key={j._id}
                                        onPress={() => {
                                            navigation.navigate("PrivateConversation", {
                                                chats: i.chat,
                                                userId: userId,
                                                socket: socket,
                                                name: j.firstName,
                                                recieverId: j._id
                                            })
                                        }
                                        } >
                                        <Text key={j._id} >{j.firstName + ' ' + j.lastName}</Text>
                                    </TouchableOpacity>
                                </>
                            )
                        }
                    )
                )
            }
        </ScrollView>
    </View>)
}

export default ChatScreen
SAYAN
  • 35
  • 2
  • 12

3 Answers3

0

I think you are not updating state in your function

Try something like below:-

socket.on("recieve-private-message", (data) => {
  const privateMessagesCopy = [...privateMessages]
  const updatedPrivateMessages = privateMessagesCopy.map((i) => {
    if (i.pair[0]._id == data.sender) {
      i.chat.unshift(data.chatObj)
    }
    return i;
  })
  setPrivateMessage([...updatedPrivateMessages]);
})

Priyank Kachhela
  • 2,517
  • 8
  • 16
  • in your useEffect can you try after updating dependency to `privateMessages.chat` – Priyank Kachhela May 10 '21 at 11:03
  • I am using socket.io which needs only to be initialized at the start and also i am fetchinng data which only needs to be done in intial render and there is no variable called that – SAYAN May 10 '21 at 11:08
  • I am talking about other useEffect in which you are consoling changes message. This one `useEffect(() => {console.log("changed")},[privateMessages.chat])` – Priyank Kachhela May 10 '21 at 11:09
  • But that doesn't solve the main problem , I just put it there for debugging – SAYAN May 10 '21 at 11:22
  • yes, I just want to check after updating state does that useEffect working so can you please change dependency in that useEffect and check if it is working? – Priyank Kachhela May 10 '21 at 11:32
  • I tried to print the whole array using that dependency and it was just infinity printing it and i forcefully had to stop the server – SAYAN May 10 '21 at 11:35
  • Sorry but i forgot to check if it had updated the array – SAYAN May 10 '21 at 11:36
0

I would avoid the force update pattern as much as possible. I don’t think you need it here. While I can’t see anything glaringly wrong, I would recommend you try these two options to help debug. If these solutions don’t work, then there’s something else very wrong.

(1) Use setState instead. When you update the chat object, create a deep copy of it first. Update the deep copy with your current method and then call setState(copy). Note: make sure to use a library to deep copy like lodash or the stringify trick (What is the most efficient way to deep clone an object in JavaScript?)

(2) Use reducers instead. When you have complicated objects like yours, reducers are very commonly used. This is probably the best solution in terms of best practices (maintainability and readability)

Punisher
  • 654
  • 5
  • 21
0

Change your socket function to:

socket.on("recieve-private-message", (data) => {
  setPrivateMessages(prevState => prevState
     .map(privateMessage => {
        const [firstPair] = privateMessage.pair

        return firstPair._id !== data.sender
            ? privateMessage
            : ({
                ...privateMessage,
                chat: [
                    data.chatObj,
                    ...privateMessage.chat
                ]
            })
     })
  )
})
Jacek Pudysz
  • 196
  • 1
  • 11