Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
168 views
in Technique[技术] by (71.8m points)

React native: Flatlist does not update instantly when i send a message

I just finishing setting up my socket in my react native and nodejs project and still my flatlist does not update instantly when a message is sent, i need to refresh the app in order for it to update.

I thought that by using socket this will work but still it is not working. whenever i user opens a chat i get eg. user: 1 has joined conversation 1 in the console which indicates that the socket is working.

Client Side

function ChatScreen({route,navigation}) {
const message = route.params.message;

const [messages, setMessages] = useState(message.Messages);
const [text, setText] = useState('');
const [socket, setSocket] = useState(null);
const { user } = useAuth();

const index = route.params.index;
const updateView = route.params.updateView;

useEffect(() => {
const newsocket =io.connect(socketURL)
setMessages(messages);
newsocket.on('connect', msg => {
    console.log(`user: ${user.id} has joined conversation ${message.id}`)
    setMessages(messages=>messages.concat(msg))
    setSocket(newsocket)
 });
 return()=>newsocket.close;
 }, []);

const onSend = (ConversationId,senderId,receiverId,message) => {

messagesApi.sendMessage({ConversationId,senderId,receiverId,message});
setText("")

const to = (user.id===route.params.message.user1? 
route.params.message.user2:route.params.message.user1)

socket.emit('message', { to: to, from: user.id, message, ConversationId });
};

const updateText=(text)=>{
setText(text);
}

return (
    <FlatList
    inverted
    data={message.Messages}
    keyExtractor={(message) => message.id.toString()}
    renderItem={({item,index})=>(
        <>
        <Text>
         {moment(item.createdAt).fromNow()}
         </Text>
        <MessageBubble
        text={item.message}
        mine={item.senderId !== user.id}
        />
         </>
    )}      
    />
    <View style={styles.messageBoxContainer}>
        <TextInput 
        onChangeText={updateText} 
        value={text} 
        />
        <TouchableOpacity 
          onPress={()=>{onSend(message.id,user.id,(user.id===message.user1? 
          message.user2:message.user1),text)}}>
        </TouchableOpacity>
    </View>
);
}

Server Side

const express = require("express");
const app = express();
const http = require("http");
const socket = require("socket.io")
const server=http.createServer(app);
const io =socket(server)

io.on('connection', (socket) => {
console.log("connected")
socket.on('message', (data) => {
console.log(data)
  socket.join(data.ConversationId);
  io.sockets.in(data.to).emit('send_message', { message: 
  data.message, to: data.to });
  });
  });

UPDATE

Client Side

  const message = route.params.message;
  const [messages, setMessages] = useState([]);
  const [text, setText] = useState('');
  const [socket, setSocket] = useState(null);
  const { user } = useAuth();

  useEffect(() => {
  const newsocket =io.connect("http://192.168.1.103:9000")
  newsocket.on('connect', msg => {
  console.log(`user: ${user.id} has joined conversation 
  ${message.id}`)
  setSocket(newsocket)
  setMessages(message.Messages)
  });

  newsocket.on("send_message", (msg) => {
  console.log("this is the chat message:", msg);
  setMessages([ { ...message.Messages },...messages]);
  });

  return()=>newsocket.close;
  }, []);

  const onSend = (ConversationId,senderId,receiverId,message) => {
  console.log("sent")
messagesApi.sendMessage({ConversationId,senderId,receiverId,message});
setText("")
const to = (user.id===route.params.message.user1? 
route.params.message.user2:route.params.message.user1)
    socket.emit(
    'message', { to: to, from: user.id, message,ConversationId });
  };

 const updateText=(text)=>{
 setText(text);
 }

<FlatList
    inverted
    data={messages}
    keyExtractor={(message) => message.id.toString()}
    renderItem={({item,index})=>(
        <>
        <Text>
         {moment(item.createdAt).fromNow()}
         </Text>
        <MessageBubble
        text={item.message}
        mine={item.senderId !== user.id}
        />
         </>
    )}   
    bounces={false}  
    />
    <View style={styles.messageBoxContainer}>
        <TextInput 
        onChangeText={updateText} 
        value={text} 
        />
        <TouchableOpacity 
         onPress={()=>{
         onSend(
         message.id,
         user.id, 
         (user.id===message.user1?message.user2:message.user1),
         text
         )}}
         >
         <Text>Send</Text>
        </TouchableOpacity>
     </View>

Server Side

io.on('connection', (socket) => {
console.log("connected")
socket.on('message', (data) => {
console.log(data)
socket.emit('send_message', { message: data.message, receiverId: 
data.to,senderId:data.from,conversationId:data.ConversationId })
});
});

Using the updated code, when i open a chat i get

user: 43 has joined conversation 4 ---- on client side console
connected ---- on server side console

Using the updated code, when i send a message i get

this is the chat message: Object {
"conversationId": 25,
"message": "You",
"receiverId": 47,
"senderId": 43,
} --- in my client side console

{ to: 47, from: 43, message: 'You', ConversationId: 25 } ---- server 
side console

But then i get an error

undefined is not an object (evaluating 'message.id.toString')

I think my problem is that i am not emitting the the message id correctly and therefore my flatlist does not know it. To get a message id, i need to store the message in db first

NEW UPDATE

Client Side

  const message = route.params.message;
  const [messages, setMessages] = useState([]);
  const [text, setText] = useState('');
  const [socket, setSocket] = useState(null);
  const { user } = useAuth();

  useEffect(() => {
  const newsocket =io.connect(socketURL)
  newsocket.on('connect', msg => {
  console.log(`user: ${user.id} has joined conversation 
  ${message.id}`)
  setSocket(newsocket)
  setMessages(message.Messages)
  });

  newsocket.on("send_message", (msg) => {
  console.log("this is the chat message:", msg);
  const data = [...messages]; 
  console.log(data)
  data.push(msg);
  setMessages(data); 
  });

  return(()=>newsocket.close());
  }, []);

  const onSend = (ConversationId,senderId,receiverId,message) => {
  console.log("sent")
messagesApi.sendMessage({ConversationId,senderId,receiverId,message});
setText("")
const to = (user.id===route.params.message.user1? 
route.params.message.user2:route.params.message.user1)
    socket.emit(
    'message', { to: to, from: user.id, message,ConversationId });
  };

 const updateText=(text)=>{
 setText(text);
 }

<FlatList
    inverted
    data={messages}
    keyExtractor={(item,index)=>index.toString()}
    renderItem={({item,index})=>(
        <>
        <Text>
         {moment(item.createdAt).fromNow()}
         </Text>
        <MessageBubble
        text={item.message}
        mine={item.senderId !== user.id}
        />
         </>
    )}   
    bounces={false}  
    />
    <View style={styles.messageBoxContainer}>
        <TextInput 
        onChangeText={updateText} 
        value={text} 
        />
        <TouchableOpacity 
         onPress={()=>{
         onSend(
         message.id,
         user.id, 
         (user.id===message.user1?message.user2:message.user1),
         text
         )}}
         >
         <Text>Send</Text>
        </TouchableOpacity>
     </View>

Server Side

io.on('connection', (socket) => {
console.log("connected")
socket.on('message', (data) => {
console.log(data)
socket.emit('send_message', { message: data.message, receiverId: 
data.to,senderId:data.from,conversationId:data.ConversationId })
});
});

Now using the new updated code when i send a message only the new message gets rendered to the sender without the previous messages, and the receiver does not receive anything while on the chat.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

So from your answers I get the impression that in order to make it work you would need to do roughly the following:

  1. On your client side you gotta have a socket listening for updates:

socket.on('send_message', (e) => doSomethingWith(e));

  1. Once you have your socket listening to updates, you can treat the information received. The easiest way to make sure your component rerenders and shows the correct information is to use state. So upon receiving the message from the socket, you could set the messages

setMessages(socketMessage)

  1. You then pass messages state to your data in Flatlist. I would also pass it in the extraData field also in Flatlist. If you have redux in place, you may want to save the messages there, then there will be no need to set the local state in the component and you could just take it from props.

Hope this helps, if you have any additional questions don't hesitate to ask them.

Edit 1:

I'm just gonna copy the code from the solution you mentioned above:

useEffect(() => {
    handleGetGroupMessage();
    let socket = io(socketUrl);
    socket.on("GroupChat", (msg) => {
      console.log("this is the chat messages", chatMessages);
      setChatMessages(chatMessages.concat(msg)); // this is the "doSomethingWith(e)"
    });
  }, []);

You receive your message via the socket and then have to do something with it. Not using redux? Set it to local state or to top level context, whatever works for your app.

Edit 2: socket.on('connect', ...) is a standard event which socket understands. It's not your messages, you need to establish that apart.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...