Dynamically update context in React Native hook - reactjs

I am trying to update theme of my react native app using context API but it is throwing an error setThemeMode is not a function. (In 'setThemeMode(themeMode === 'light' ? 'dark': 'light')', 'setThemeMode' is "i")
I have taken refernce of following blog article
https://www.smashingmagazine.com/2020/01/introduction-react-context-api/
Main Error Image
ThemeContext.js
import React from 'react';
const ThemeContext = React.createContext(['light', () => {}]);
export default ThemeContext;
App.js
import React, {useState} from 'react';
import Nav from './src/navigation/Nav';
import 'react-native-gesture-handler';
import ThemeContext from './src/context/ThemeContext';
const App = () => {
const [theme] = useState("light");
return (
<>
<ThemeContext.Provider value={theme}>
<Nav />
</ThemeContext.Provider>
</>
);
};
export default App;
Settings.js
import React, {useContext} from 'react';
import {View, Text, TouchableHighlight, Alert} from 'react-native';
import Icon from 'react-native-vector-icons/dist/Ionicons';
import Switches from 'react-native-switches';
import ThemeContext from './../context/ThemeContext';
import AppTheme from './../Colors';
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
} from 'react-native-responsive-screen';
import ThemeSwitch from './ThemeSwitch';
const Settings = () => {
const [themeMode, setThemeMode] = useContext(ThemeContext);
const theme = useContext(ThemeContext);
const currentTheme = AppTheme[theme];
return (
<>
<TouchableHighlight
onPress={() => setThemeMode(themeMode === 'light' ? 'dark' : 'light')}
style={{
backgroundColor: 'black',
borderRadius: 100,
width: wp(14),
height: wp(14),
justifyContent: 'center',
alignItems: 'center',
}}>
<Icon name="md-arrow-round-back" size={wp(8)} color="white" />
</TouchableHighlight>
</>
);
};
export default Settings;
Nav.js
import React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import Welcome from './../components/Welcome';
import Settings from './../components/Settings';
import Main from './../components/Main';
const Stack = createStackNavigator();
const Nav = () => {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false,
}}>
<Stack.Screen name="Main" component={Main} />
<Stack.Screen name="Settings" component={Settings} />
<Stack.Screen name="Welcome" component={Welcome} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default Nav;
Colors.js
const AppTheme = {
light: {
name: 'light',
textColor: 'black',
backgroundColor: 'white',
},
dark: {
name: 'dark',
textColor: 'white',
backgroundColor: 'black',
},
};
export default AppTheme;
I want to dynamically update context. Pardon me for such silly bug but I am new to react and Js.
I have attached the issue image. I think I am doing something wrong with useContext because when I try to console.log(ThemeContext) it was showing undefined instead of light.

In App js ... You have to set the theme mode like
const [themeMode, setThemeMode] = useState('light');
then
<ThemeContext.Provider value={themeMode,setThemeMode}>
then wherever you want to update the value ... you can access it
const [theme,setThemeMode] = useContext(ThemeContext)
instead of create and assign state to context use the state from Context

const [themeMode, setThemeMode] = useContext(ThemeContext);
Should be
const [themeMode, setThemeMode] = useState(ThemeContext);

Related

How to stop a global component being rendered on a particular screen?

I'm using react-native-toast-notifications package for showing in-app notifications.
I'm trying to integrate it for showing notifications when a chat arrives. But this notification is also being displayed on the chat room screen where the one-to-one chat happens.
How can I prevent the notification being appeared on the chat room screen?
This is my provider component
<ToastProvider
swipeEnabled={true}
placement="top"
duration={5000}
animationType="zoom-in"
animationDuration={300}
renderType={{
message_toast: (toast) => <ToastContainer toast={toast} />,
}}
>
<SymbolsProvider>
<Navigation />
</SymbolsProvider>
</ToastProvider>
I'm using the useToast() hook provided by the package to show notifications
const toast = useToast();
toast.show(socketMessage.username + " Sent a message", {
position: "bottom",
duration: 3000,
style: toastStyle,
type: "success",
});
Sent a message
This notification is appearing on the chat room screen also which should not be happening.
Here's an example base on the comment under the question:
import { ToastProvider } from 'react-native-toast-notifications'
import {NavigationContainer} from '#react-navigation/native'
import Screens from './screens/index'
export default function App() {
return (
<ToastProvider>
<NavigationContainer>
<Screens />
</NavigationContainer>
</ToastProvider>
);
}
import React from 'react';
import {
SafeAreaView,
View,
StyleSheet,
TouchableOpacity,
Text,
} from 'react-native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { useNavigation } from '#react-navigation/native';
import { useToast } from 'react-native-toast-notifications';
const Stack = createNativeStackNavigator();
const Drawer = createDrawerNavigator();
const ToastButton = ({ style}) => {
const toast = useToast();
const navigation = useNavigation();
const navState = navigation.getState();
console.log(navState.routeNames);
const currentRoute = navState.routeNames[navState.index];
const onPress = () => {
if (currentRoute != 'Chat') toast.show('Toast from ' + currentRoute);
};
return (
<TouchableOpacity style={[styles.container, style]} onPress={onPress}>
<Text>Show Toast</Text>
</TouchableOpacity>
);
};
const HomeScreen = (props) => {
return (
<View style={styles.flex}>
<ToastButton />
</View>
);
};
const ChatScreen = (props) => {
return (
<View style={styles.flex}>
<ToastButton />
</View>
);
};
export default function ScreenIndex(props) {
return (
<SafeAreaView style={{ flex: 1 }}>
<Drawer.Navigator useLegacyImplementation={true}>
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Chat" component={ChatScreen} />
</Drawer.Navigator>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
flex: {
flex: 1,
backgroundColor: '#eef',
},
});

Problem Upgrading React application - material UI v4 to v5

I have this problem upgrading my react application. Basically, I followed the damn mui-v5 documentation, and I still have some erros in my app.tsx file.
import { useReactOidc, withOidcSecure } from '#axa-fr/react-oidc-context';
import { Container } from '#mui/material';
import CssBaseline from '#mui/material/CssBaseline';
import {
ThemeProvider,
StyledEngineProvider,
Theme,
} from '#mui/material/styles';
import createStyles from '#mui/styles/createStyles';
import makeStyles from '#mui/styles/makeStyles';
import { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { Redirect, Route, Switch } from 'react-router-dom';
import { RootState } from 'src/lib/configuration/store-configuration';
import { DarkTheme, LightTheme } from 'src/components/material-ui-theme';
import routes from 'src/components/routes/routes';
import BreadCrumbs from 'src/components/shared/breadcrumbs/breadcrumbs';
import ErrorBoundary from 'src/components/shared/error/Error';
import Footer from 'src/components/shared/footer/footer';
import Header from 'src/components/shared/header/header';
import Notifier from 'src/components/shared/notifications';
import ScrollTop from 'src/components/shared/scroll-top/scroll-top';
import Sider from 'src/components/shared/sider';
import { useTranslation } from 'react-i18next';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
display: 'flex',
},
toolbar: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar,
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
},
container: {
paddingTop: theme.spacing(4),
paddingBottom: theme.spacing(1),
},
}),
);
const App = () => {
const classes = useStyles();
const { oidcUser } = useReactOidc();
const appStore = useSelector((store: RootState) => store.app);
const [drawerOpen, setDrawerOpen] = useState(false);
const handleDrawerToggle = () => {
setDrawerOpen(!drawerOpen);
};
const { i18n } = useTranslation();
useEffect(() => {
document.documentElement.lang = i18n.language;
}, [i18n.language]);
return (
<StyledEngineProvider injectFirst>
<ThemeProvider
theme={
appStore.theme === 'dark' ? DarkTheme : LightTheme
}
>
<div className={classes.root}>
<CssBaseline />
<Header handleDrawerToggle={handleDrawerToggle} />
{oidcUser ? (
<Sider
drawerOpen={drawerOpen}
handleDrawerToggle={
handleDrawerToggle
}
/>
) : null}
<main className={classes.content}>
<div id="back-to-top-anchor" />
<div className={classes.toolbar} />
<Container
maxWidth="xl"
className={classes.container}
>
<Notifier />
<BreadCrumbs />
<ErrorBoundary>
<Switch>
{routes.map(
(route, i) => (
<Route
key={
'route_' +
i
}
exact={
route.exact
}
path={
route.path
}
component={
route.secure
? withOidcSecure(
route.component,
)
: route.component
}
/>
),
)}
<Redirect to="/" />
</Switch>
</ErrorBoundary>
</Container>
<ScrollTop />
<Footer />
</main>
</div>
</ThemeProvider>
</StyledEngineProvider>
);
};
export default App;
Here is the navigator console error :
I tried to modify some imports and used:
import { createTheme } from '#material-ui/core';
const theme = createTheme();
And passing it to makestyles((theme) => ... but nothing changed.
Please guys I need your support ! :)
link for the open-source github project: https://github.com/InseeFr/sugoi-ui

navigation.push is not a function. (In 'navigation.push("EditProfile")', 'navigation.push' is undefined) (React navigation V6)

I'm new to React Native and I'm trying to perform a simple stack navigation. I got it to work in another part of the application (User authentication step). My code then feeds onto another stack once the user is logged in. This stack navigator has a tab navigator nested in which may be causing the problem?
Either way I can't perform a push, from my profile screen to the edit profile screen. Code is below.
import React from 'react'
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import LoginScreen from './screens/LoginScreen';
import HomeScreen from './screens/HomeScreen';
import SignupScreen from './screens/SignupScreen';
import ProfileScreen from './screens/ProfileScreen';
import EditProfileScreen from './screens/EditProfileScreen';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import Icon from 'react-native-vector-icons/Ionicons';
const Stack = createNativeStackNavigator()
const screenOptions = {
headerShown: false
}
export const SignedOutStack = () =\> (
\<NavigationContainer\>
\<Stack.Navigator
initialRouteName="LoginScreen"
screenOptions={screenOptions}
\\>
\<Stack.Screen
name="Login"
component={LoginScreen}
/\>
\<Stack.Screen
name='SignupScreen'
component={SignupScreen}
/\>
\</Stack.Navigator\>
\</NavigationContainer\>
)
const Tab = createBottomTabNavigator();
export const SignedInStack = () =\> (
\<NavigationContainer\>
\<Tab.Navigator
screenOptions={screenOptions}\>
\<Tab.Screen name="Home" component={HomeScreen} /\>
\<Tab.Screen name="Profile" component={ProfileScreen} /\>
\</Tab.Navigator\>
\</NavigationContainer\>
)
export const ProfileStack = () =\> (
\<NavigationContainer\>
\<Stack.Navigator\>
\<Stack.Screen name="SignedInStack" component={SignedInStack} /\>
\<Stack.Screen name="EditProfile" component={EditProfileScreen} /\>
\</Stack.Navigator\>
\</NavigationContainer\>
)
Where I'm trying to implement the push
import { View, Text, SafeAreaView, ScrollView, StyleSheet, Image, TouchableOpacity, StatusBar, Button } from 'react-native'
import React, {useState, useEffect, useContext} from 'react';
import EditProfileScreen from './EditProfileScreen';
const ProfileScreen = ({navigation}) =\> (
// const {user, logout} = useContext(AuthContext)
<SafeAreaView style={styles.wrapper}>
<ScrollView
style={styles.container}
contentContainerStyle={{justifyContent: 'center', alignItems: 'center'}}
showsVerticalScrollIndicator = {false}
>
<Image style={styles.userImg} source={{uri: 'https://www.dmarge.com/wp-content/uploads/2021/01/dwayne-the-rock-.jpg'}} />
<Text style={styles.userName}>Person</Text>
<View style={styles.userBtnWrapper}>
<Button title="Edit Profile" onPress = {() => {
navigation.push("EditProfile")
}}/>
</View>
</ScrollView>
</SafeAreaView>
)
export default ProfileScreen
You may need to declare a stackAction before actually use navigation.
import { StackActions } from '#react-navigation/native';
....
const pushAction = StackActions.push('Profile', { user: 'Wojtek' });
navigation.dispatch(pushAction);

How to load custom Fonts in an Expo app using React Navigation and Redux

I just can not load custom fonts in an existing app using Expo, React Natvigation. I have an app that has Navigations set up in a core.js file as
import React from "react";
import {Text, View, StyleSheet} from "react-native";
import {NavigationContainer} from "#react-navigation/native";
import {createStackNavigator} from "#react-navigation/stack";
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import LoginScreen from "./login.js";
import RegisterScreen from "./register.js";
//import Home from "./home.js";
import PostAJobPage from "./secondTab.js";
import Ionicons from "react-native-vector-icons/Ionicons"
import store from "./store";
import SplashScreen from "./splashscreen";
import Home from "./homeStack.js";
import { Entypo } from '#expo/vector-icons';
import { MaterialCommunityIcons } from '#expo/vector-icons';
import SearchAJobStack from "./jobsearchstack.js";
const Tab = createBottomTabNavigator()
const Tabs = () => {
return(
//<NavigationContainer>
<Tab.Navigator initialRouteName = "home"
screenOptions = {({route}) => ({
tabBarIcon : ({focused,color, size}) => {
let iconName;
if(route.name == "home") {
iconName = "ios-home"
return <Ionicons name = {iconName} color = {color} size = {size}/>
}
else if(route.name == "postJob")
{
//iconName = "md-settings"
iconName = "briefcase"
return <Entypo name = {iconName} color = {color} size = {size}/>
}
else if (route.name == "searchJob")
{
iconName = "briefcase-search"
return <MaterialCommunityIcons name = {iconName} size={size} color= {color} />
}
//return <Ionicons name = {iconName} color = {color} size = {size}/>
}
})}
tabBarOptions = {{
style : {
height : 50
},
showLabel : false,
activeTintColor : "gold"
}
}>
<Tab.Screen name="home" component ={Home}/>
<Tab.Screen name="postJob" component ={PostAJobPage}/>
<Tab.Screen name="searchJob" component ={SearchAJobStack}/>
</Tab.Navigator>
//</NavigationContainer>
)
}
const Stack = createStackNavigator()
function Stacks() {
return(
<NavigationContainer>
<Stack.Navigator initialRouteName = "loadingPage">
<Stack.Screen
name = "loadingPage"
component = {SplashScreen}
options = {{
headerShown : false
}}/>
<Stack.Screen
name ="Main"
component = {Tabs}
options = {{
headerShown : false,
}}/>
<Stack.Screen name= "login"
component = {LoginScreen}
options = {{
title : "Login",
headerTitleAlign : "left"
}}/>
<Stack.Screen name= "register"
component = {RegisterScreen}
options = {{
title : "Register",
headerTitleAlign : "left",
}}/>
</Stack.Navigator>
</NavigationContainer>
)
}
export default class core extends React.Component {
render(){
return (
<Stacks/>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor : "#fff",
alignItems : "center",
justifyContent : "center",
}
})
I am importing this stacks in my App.js and have added the code to add custom fonts according to the Docs in Expo but if I change fontFamily: Pacifico to any of the screens it doesnot work
My app.js file is
import 'react-native-gesture-handler';
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
import Core from "./core.js";
import {Provider} from "react-redux";
import store from "./store.js";
import * as Font from "expo-font";
const customFonts = {
dancingScript : require("./assets/fonts/DancingScript-Regular.ttf"),
Pacifico : require("./assets/fonts/Pacifico-Regular.ttf"),
Inter : require("./assets/fonts/Inter-VariableFont.ttf"),
}
export default class App extends React.Component {
state = {
fontsLoaded : false,
}
async _loadFontsAsync() {
await Font.loadAsync(customFonts);
this.setState({ fontsLoaded: true });
}
componentDidMount() {
this._loadFontsAsync();
}
render(){
if(this.state.fontsLoaded)
{
return (
<Provider store = {store}>
<Core/>
</Provider>
);
}
else {
return(
<View style= {styles.container}>
<Image source = {require("./ajax-loader.gif")}/>
</View>
)
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
//fontFamily : "dancingScript",
},
});
I personally feel this is a better way to load custom fonts. I use this method in all my projects. To loadFonts create a folder called hooks where your App.js is located
Then, Install expo-app-loading & expo-font
Then inside hooks folder create a file called useFonts.js
Inside useFonts.js write like this
import * as Font from "expo-font";
export default useFonts = async () => {
await Font.loadAsync({
"Pacifico" : require("./assets/fonts/Pacifico-Regular.ttf"),
// All other fonts here
});
};.
Now in your App.js write like this
import * as Font from 'expo-font';
import AppLoading from 'expo-app-loading';
import React, { useState } from 'react';
import useFonts from './hooks/useFonts';
export default function App() {
const [IsReady, SetIsReady] = useState(false);
const LoadFonts = async () => {
await useFonts();
};
if (!IsReady) {
return (
<AppLoading
startAsync={LoadFonts}
onFinish={() => SetIsReady(true)}
onError={() => {}}
/>
);
}
return <View styles={styles.container}>{/* Code Here */}</View>;
}
Note: The above solution was for Function components.
For Class Component, you can do something like this
Working Example Here
import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
import useFonts from './hooks/useFonts';
export default class App extends React.Component {
state = {
fontsLoaded: false,
};
async _loadFontsAsync() {
await useFonts();
this.setState({ fontsLoaded: true });
}
componentDidMount() {
this._loadFontsAsync();
}
render() {
if (this.state.fontsLoaded) {
return (
<View style={styles.container}>
<Text>Fonts Loaded</Text>
<Text style={{ fontFamily: 'Helvetica' }}>Helvetica Text</Text>
<Text>Normal Text</Text>
</View>
);
} else {
return (
<View style={styles.container}>
<Image
source={{
uri:
'https://landerapp.com/blog/wp-content/uploads/2018/06/1_FFP1bisztXseQFbZ-WQedw-1.png',
}}
style={{ width: '100%', height: '100%' }}
/>
</View>
);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
//fontFamily : "dancingScript",
},
});
this is how you can load your fonts in an expo app write the code in the entry point of your app which is mainly App.js
import { useFonts } from "expo-font";
const [fontsLoaded] = useFonts({
// Axiforma: require("./assets/fonts/Axiforma-Black.ttf"),
// BlackItalic: require("./assets/fonts/Axiforma-BlackItalic.ttf"),
Bold: require("./assets/fonts/Axiforma-Bold.ttf"),
// BoldItalic: require("./assets/fonts/Axiforma-BoldItalic.ttf"),
// Book: require("./assets/fonts/Axiforma-Book.ttf"),
// BookItalic: require("./assets/fonts/Axiforma-BookItalic.ttf"),
ExtraBold: require("./assets/fonts/Axiforma-ExtraBold.ttf"),
// ExtraBoldItalic: require("./assets/fonts/Axiforma-ExtraBoldItalic.ttf"),
// Heavy: require("./assets/fonts/Axiforma-Heavy.ttf"),
// HeavyItalic: require("./assets/fonts/Axiforma-HeavyItalic.ttf"),
// Italic: require("./assets/fonts/Axiforma-Italic.ttf"),
Light: require("./assets/fonts/Axiforma-Light.ttf"),
// LightItalic: require("./assets/fonts/Axiforma-LightItalic.ttf"),
Medium: require("./assets/fonts/Axiforma-Medium.ttf"),
// MediumItalic: require("./assets/fonts/Axiforma-MediumItalic.ttf"),
Regular: require("./assets/fonts/Axiforma-Regular.ttf"),
SemiBold: require("./assets/fonts/Axiforma-SemiBold.ttf"),
// SemiBoldItalic: require("./assets/fonts/Axiforma-SemiBoldItalic.ttf"),
Thin: require("./assets/fonts/Axiforma-Thin.ttf"),
// ThinItalic: require("./assets/fonts/Axiforma-ThinItalic.ttf"),
});
if (!fontsLoaded) return <AppLoading />;
Use #expo-google-fonts
Go to the project root from the terminal and type to install the font that wish, example:
expo install #expo-google-fonts/ubuntu
The explo cli will install all ubuntu fonts as a node module, look inside the path ./node_modules/#expo-google-fonts/ubuntu, all fonts are there.
Now, import the hook and the font styles that you wish from this font family, like this:
import { useFonts, Ubuntu_400Regular, Ubuntu_500Medium, Ubuntu_700Bold } from '#expo-google-fonts/ubuntu';
Then, load them inside your app using the hook:
let [fontsReady,fontsError] = useFonts({Ubuntu_400Regular, Ubuntu_500Medium, Ubuntu_700Bold});
fontsReady will be true when the fonts are ready to use and the fontsError will has the error reason if the app could not load the fonts.
To wait the fonts be ready, use expo-app-loading module and do something like this:
if (!fontsReady) return <AppLoading/>;
To choose the fonts that you wish to install on your project, visit https://directory.vercel.app/ , this will show all Google fonts and the import cmd in an easy way. Do not forget to include the useFonts hook.

React native navigation error- navigating issue

I am trying to create react native application.But it is throwing error. I used react-native-navigation.
(I am trying to create react native application.But it is throwing error. I used react-native-navigation. )
import { createAppContainer, createSwitchNavigator } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";
import { createBottomTabNavigator } from "react-navigation-tabs";
import DashboardScreen from "./src/component/Dashboard";
import LoginScreen from "./src/component/Auth/Login";
import RegisterScreen from "./src/component/Auth/Register";
const AppStack = createStackNavigator({ Dashboard: DashboardScreen });
const AuthStack = createBottomTabNavigator({
SignIn: LoginScreen,
Register: RegisterScreen
});
export const RootNaviation = () => {
return createAppContainer(
createSwitchNavigator(
{
Auth: AuthStack,
App: AppStack
},
{ initialRouteName: "Auth" }
)
);
};
App File
import React from "react";
import { StyleSheet, Text, View, Button } from "react-native";
import { SwitchNavigator } from "./Router";
const Nav = SwitchNavigator();
export default function App() {
return (
<View style={styles.container}>
<Button title="dafdlka" />
<Nav />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
}
});
enter image description here
You have never created a variable called SwitchNavigator.
You can use RootNaviation
import { RootNaviation } from "./Router"
...
export default function App() {
return (
<View style={styles.container}>
<Button title="dafdlka" />
<RootNaviation />
</View>
);
}

Resources