简体   繁体   中英

React Native: How to access a variable from parent component in child component?

i am trying to pass my newsocket variable from my MessagesScreen.js to my ChatScreen.js.

I have been stuck on this point for a while and would appreciate any help possible. What i am trying to achieve is that only one connection gets emitted which i can listen to events on both screen.

The connection is now opened on the messagesScreen. My problem now is if user 1 is on the allmessages screen and user 2 is inside the chat. And user 2 sends user 1 a message, user 1's screen does not automatically update with the last message for the conversation the message was sent to, I need to either scroll to refresh or navigate from one page to the other in order for it to appear.

Here is my code:

PARENT --> messagesScreen.js

function MessagesScreen({navigation}) {
const [posts, setPosts] = useState([]);
const { user } = useAuth();
const [socket, setSocket] = useState(null);

const loadposts = async () => {
const response = await messagesApi.getMessages();// here i am loading all the conversation this user has
setPosts(response.data)
};

useEffect(() => {

newsocket = sockets(user.id); // newsocket is what i am trying to pass to child component
setSocket(newsocket);

loadPosts()

newsocket.on("send_message", (msg) => {
  console.log("messages:", msg);
})

}, []);

return (
<FlatList
    data={posts}
    keyExtractor={(post) => post.id.toString()}
    renderItem={({ item,index }) => (
      <MessagesList
      title={item.Post.title}
        subTitle={item.Messages[0].message}
        onPress={() => navigation.navigate(
                routes.CHAT,{message:item,index,newsocket:socket})}
      />
    )}
  />
)

CHILD ---> chatScreen.js

function ChatScreen({route,navigation,socket}) {  
const [messages, setMessages] = useState([]);
const { user } = useAuth();

  const index = route.params.index;
  const message = route.params.message;
  const newsocket = route.params.newsocket;

  const loadListings = async () => {
  const response = await messagesApi.getConversation(message.id);// here i am loading the messages in that specific conversation
  setMessages(response.data.Messages)
  };

 useEffect(() => {
 loadListings()
 newsocket.emit('subscribe', message.id);

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

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

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});

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

return(
 <FlatList
    inverted
    data={messages}
    keyExtractor={(item,index)=>index.toString()}
    extraData={messages} // add this    
    renderItem={({item,index})=>(
        <MessageBubble
        text={item.message}
        mine={item.senderId !== user.id}
        />
    )}
    />
)

socket.js

import io from 'socket.io-client';

const newsocket = (user) => {
let newsocket = io.connect("http://192.168.1.107:9000")

newsocket.on('connect', msg => {
console.log(`waiting for user: ${user} to join a conversation`)
});

newsocket.emit('waiting', user);

return newsocket;
}

export default newsocket;

I would approach this differently.

  1. You can create your socket connection as a shared service in a separate module and simply import that into the relevant components you need. In this shared module you handle connecting/disconnecting and return an existing connection or create a new connection to return.

Quick rough:

// socket-server.ts
import io from 'socket.io-client';

let socket: SocketIOClient.Socket = null;

export const getSocketServer = (): Promise<SocketIOClient.Socket> => {
    return new Promise<SocketIOClient.Socket>(resolve => {
        if (socket) {
            console.info(`returning existing socket: ${socket.id}`);

            return resolve(socket);
        }

        socket = io('http://localhost:4000', {
            autoConnect: false,
        });

        socket.on('connect_error', (err) => {
            console.error(err);
        })

        socket.on('connect', () => {
            console.info(`creating new socket: ${socket.id}`);

            return resolve(socket);
        });

        socket.open();
    })
}

// then in your relevant modules
// module-a.ts
import React, {useEffect, useState} from 'react';
import {getSocketServer} from './../components/socket-server';

const Component = () => {
    useEffect(() => {
        const connect = async () => {
            const socket = await getSocketServer();

            socket.on('hello', (message) => {
                console.info('hello from module A', message);
            });
        }
        connect();
    }, []);

    return (
        <>
            <h2>Module A</h2>
        </>
    )
}

export default Component;
  1. You could maybe also look at creating a Context Provider and share the socket with relevant modules as needed.

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

On the MessagesScreen screen you are passing the SOCKET function and not the variable it self. i think you do not need the function. you directly pass the variable and access in chatScreen screen.

MessagesScreen.js

routes.CHAT,{message:item,index,updateView, newsocket})}

chatScreen.js

const newsocket = route.params.newsocket;
 ....

newsocket.emit('subscribe', message.id);  // call like this

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM