How to use the same socket on different pages in react native - react-native

I am using Socket.io for chatting feature in my react-native project. My project is using react native navigation.
But I have trouble things passing Socket.io socket to certain screens. I want to share the socket with the navigation( like a prop I think I'm not sure ).
Currently I'm using Socket.io socketes on each certain screens individually. But this way has some troubles.
The main trouble is that when someone sends me a message I need to know which page I'm on while the app is running.
Does someone have any suggestions on how to do it?
My target is to share the same socket with three pages.
I googled, but couldn't find any suitable results.
- AppNavigation.js
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
// Chat.
import ChatScreen from '../screens/Chat/ChatScreen';
import VideoChatScreen from '../screens/Chat/VideoChatScreen;
const Stack = createStackNavigator();
function AppNavigator() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Chat" component={ChatScreen} options={{ headerShown: false, gestureEnabled: false }}/>
<Stack.Screen name="VideoChat" component={VideoChatScreen} options={{ headerShown: false, gestureEnabled: false }}/>
</Stack.Navigator>
</NavigationContainer>
);
}
export default AppNavigator;
- ChatScreen.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
import SocketIOClient from 'socket.io-client'
class ChatScreen extends Component {
constructor() {
super()
this.state = {}
this.socketClient = null;
}
componentDidMount() {
this.socketClient = SocketIOClient(url);
this.socketClient.onAny((event, params) => {
this.onResponseOnSocket(event, params);
});
}
...
- VideoChatScreen.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
class VideoChatScreen extends Component {
constructor() {
super()
this.state = {}
this.socketClient = null;
}
componentDidMount() {
this.socketClient = SocketIOClient(url);
this.socketClient.onAny((event, params) => {
this.onResponseOnSocket(event, params);
});
}
...

You can use a state management tool of your choice to manage a global (App-)State within your app. With functional components I like ContextProvider which is part of RN Core and is still o.k. for class components as long as you have only a few Provider. But many people use Redux or you can use facebooks Recoil which is also an interesting alternative.
https://reactjs.org/docs/context.html
https://redux.js.org/introduction/getting-started
https://recoiljs.org/

I think my library will be perfect for your case.
It is the state managment tool dedicated to chat applications: https://github.com/chatscope/use-chat
Your application should be wrapped with ChatProvider component, and you need to create your own service/adapter that will be responsible for websocket connection.
Sending messages, message list, conversations etc. are available in all child components with one hook.

Related

React Native navigation ref always null

I have the following:
import {navigationRef} from "./wherever"
<NavigationContainer ref={navigationRef}>
In the navigation file I import, I have this
import React from 'react';
const navigationRef = React.createRef();
const navigate = (name, params) => {
console.log(navigationRef)
navigationRef.current?.navigate(name, params);
}
export default {
navigate
}
But when in my code I try to call the following:
import navigation from './wherever';
navigation.navigate('wherever')
the console log always shows
{"current": null}
and so the navigation never kicks in. Can anyone help?
the rubber duck technique in action, I answered my own question in the act of describing it to you all!
I was simply missing an export.
export default {
navigate,
navigationRef
}
And so I changed my import like so:
import navigation from "./wherever"
<NavigationContainer ref={navigation.navigationRef}>

React-native Unable to show any content on my app.js screen

I can't seem to figure out what seems to be the issue. I am getting a render error, "Could'nt find a 'component'; get component or children prop for the screen dashboard. This can happen if you passed 'undefined'. You likely forgot to export your component from the file it's defined.
My code in App.js is as follows:
```
import React from "react";
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import {
MainLayout
} from "./screens";
const Stack = createNativeStackNavigator();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false
}}
initialRouteName={'Dashboard'}
>
<Stack.Screen
name="Dashboard"
component={MainLayout}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
export default App
```
My code in /screens/Dashboard/MainLayout is as follows:
```
import React from 'react';
import {
View,
Text
} from 'react-native';
const MainLayout = () => {
return (
<View>
<Text>MainLayout</Text>
</View>
)
}
export default MainLayout;
```
You are exporting MainLayout as default so import it without { }. You can import default component with any name but non default components with exact name inside { }.
Change your import in App.js like below ( It seems that you also not given full path)
... // rest of imports
import MainLayout from "./screens/Dashboard/MainLayout";
... // rest of codes
Click here to know more about Importing and Exporting

Making navigation available to all components in React Native app

I'm using React Navigation 5 in my React Native app and I'm not exactly clear about how to make navigation available to all components. I also want to mention that my app uses Redux for state management but I didn't integrate React Navigation with Redux -- maybe I should!
My App.js renders my Navigator.js component which looks like below. BTW, my app requires authentication and one of the key functions of the navigator function is to redirect unauthenticated users to login screen.
class Navigator extends Component {
render() {
return (
<NavigationContainer>
{
this.props.isAuthenticated
? <MainMenuDrawer.Navigator drawerContent={(navigation) => <DrawerContent member={this.props.member} navigation={navigation} drawerActions={DrawerActions} handleClickLogOut={this.handleClickLogOut} />}>
<MainMenuDrawer.Screen name="Home" component={HomeStack} />
<MainMenuDrawer.Screen name="ToDoList" component={ToDoListStack} />
</MainMenuDrawer.Navigator>
: <SignInStack />
}
</NavigationContainer>
);
}
}
Then in my HomeStack, I have my Dashboard component which happens to be a class component that needs to access navigation. Here's my HomeStack:
const HomeStackNav = new createStackNavigator();
const HomeStack = () => {
return(
<HomeStackNav.Navigator screenOptions={{ headerShown: false }}>
<HomeStackNav.Screen name="DashboardScreen" component={Dashboard} />
<HomeStackNav.Screen name="SomeOtherScreen" component={SomeOtherComponent} />
</HomeStackNav.Navigator>
);
}
export default HomeStack;
Say, my Dashboard component looks like below. How do I make navigation available to this component?
class Dashboard extends Component {
handleNav() {
// Need to use navigation here...
}
render() {
return (
<Text>Welcome to Dashboard</Text>
<Button onPress={() => this.handleNav()}>Go somewhere</Button>
);
}
}
function mapStateToProps(state) {
return {
member: state.app.member
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(appActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
Do I need to pass navigation manually to each component? That seems too repetitive and prone to error. How do I make navigation available throughout my app?
Solution 1:
you can use useNavigation hook for functional component
import {useNavigation} from '#react-navigation/native';
const navigation = useNavigation();
navigation.navigate("interests");
//or
navigation.push("interests");
Solution 2:
you can use HOC withNavigation to navigation in props in any component for class component Ref
you can install #react-navigation/compat by
yarn add #react-navigation/compat
You can import like below
import { withNavigation } from '#react-navigation/compat';
you can use withNavigation like below
export default withNavigation(Dashboard)
Note: then you can use this.props.navigation in Dashboard component

How to use withNavigation in react-navigation v5?

I have a nested component, and I want to use withNavigation in the nested component in react-navigation v5.
why you don't create your own withNavigation
import React from 'react';
import { useNavigation } from '#react-navigation/native'; // not sure package name
export const withNavigation = (Component: any) => {
return (props: any) => {
const navigation = useNavigation();
return <Component navigation={navigation} {...props} />;
};
};
React Navigation Version: 5.x
Sometimes you need to trigger a navigation action from places where you do not have access to the navigation prop, such as a Redux middleware. For such cases, you can dispatch navigation actions from the navigation container.
If you're looking for a way to navigate from inside a component without needing to pass the navigation prop down. Do not use this method when you have access to a navigation prop or useNavigation since it will behave differently, and many helper methods specific to screens won't be available.
You can get access to the root navigation object through a ref and pass it to the RootNavigation which we will later use to navigate.
// App.js
import { NavigationContainer } from '#react-navigation/native';
import { navigationRef } from './RootNavigation';
export default function App() {
return (
<NavigationContainer ref={navigationRef}>{/* ... */}</NavigationContainer>
);
}
In the next step, we define RootNavigation, which is a simple module with functions that dispatch user-defined navigation actions.
// RootNavigation.js
import * as React from 'react';
export const navigationRef = React.createRef();
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}
// add other navigation functions that you need and export them
Then, in any of your javascript modules, just import the RootNavigation and call functions that you exported from it. You may use this approach outside of your React components and, in fact, it works just as well when used from within them.
// any js module
import * as RootNavigation from './path/to/RootNavigation.js';
// ...
RootNavigation.navigate('ChatScreen', { userName: 'Lucy' });
Apart from navigate, you can add other navigation actions:
import { StackActions } from '#react-navigation/native';
export function push(...args) {
navigationRef.current?.dispatch(StackActions.push(...args));
}
https://reactnavigation.org/docs/navigating-without-navigation-prop/

Context API w/ React Navigation (React Native)

I'm trying to wrap my mind around using Context in my React Native app that uses React Navigation. I think I am way off on this one. I am simply trying to pass the name of a book to my entire app through the navigation stacks.
App.js
const BookContext = React.createContext();
class BookProvider extends Component {
state = {
name: 'book name'
}
render() {
return (
<BookContext.Provider value={{
name: this.state.name
}}>
{this.props.children}
</BookContext.Provider>
)
}
}
export default function App() {
return (
<BookProvider>
<BookContext.Consumer>
{({ name }) => (<Routes name={name} />)} //my react navigation stacks component
</BookContext.Consumer>
</BookProvider>
);
}
and in Book.js (a component in the navigation stack)
componentDidMount() {
console.log(this.context)
}
returns an empty object {}
Any help is appreciated!
To save you some Googling, this is the correct approach: https://github.com/react-navigation/react-navigation/issues/935#issuecomment-359675855
Another way, if you're using a relatively new version of React, and your component in question at that route is a functional component, is to use the useContext React hook.
e.g.
import React, { useContext } from 'react'
import { BookContext } from '/path/to/BookContext'
function BookConsumerComponent() {
const { name } = useContext(BookContext);
console.log(name);
}