React Native Navigation Access Navigation from Stack.Screen - reactjs

I have a stack navigator set up like this:
<Stack.Navigator initialRouteName="Friends">
<Stack.Screen
name="Friends"
component={Friends}
options={{
title: "Friends",
headerStyle: {
backgroundColor: colors.white,
},
headerTintColor: "#fff",
headerTitleStyle: {
fontWeight: "700",
color: colors.red,
fontSize: 20,
},
headerRight: () => (
<TouchableOpacity
style={{ marginRight: 20 }}
onPress={() => /*NAVIGATE TO ADD FRIEND*/ }
>
<Ionicons name="md-person-add" size={20} color={colors.red} />
</TouchableOpacity>
),
}}
/>
<Stack.Screen
name="AddFriend"
component={AddFriend}
options={{
title: "Add Friend",
headerStyle: {
backgroundColor: colors.white,
},
headerTintColor: "#fff",
headerTitleStyle: {
fontWeight: "700",
color: colors.red,
fontSize: 20,
},
}}
/>
</Stack.Navigator>
As you can see, I have a button on the right side of the header for the Friends screen, and when that button is clicked I'd like to navigate to the Add Friend screen.
However, I don't know how to access the navigation object that you'd typically access via props.navigation inside of the component passed into Stack.Screen.
How can I achieve this?

You can access navigation like below, the options can be a function which takes navigation and route and arguments.
<Stack.Screen
name="Friends"
component={Friends}
options={({navigation})=>({
title: "Friends",
headerStyle: {
backgroundColor: colors.white,
},
headerTintColor: "#fff",
headerTitleStyle: {
fontWeight: "700",
color: colors.red,
fontSize: 20,
},
headerRight: () => (
<TouchableOpacity
style={{ marginRight: 20 }}
onPress={() => navigation.navigate('AddFriend') }
>
<Ionicons name="md-person-add" size={20} color={colors.red} />
</TouchableOpacity>
),
})}
/>

Related

useEffect not firing on state update

I am updating some state with useState() in my react native component. Once I have that state set I want to save the details to the server, so I have set that up in an useEffect() hook. To be clear, the setting of the state IS working, and I see the value print to the screen.
However, what I'm noticing is, even though I've set note as a dependency in the dependency array of the useEffect() hook, the function never fires when the state updates. What am I missing here?
const [note, setNote] = useState('');
const dispatch = useDispatch();
useEffect(() => {
if (note) {
console.log('updating note...');
dispatch(updateNote(props.client.id, note));
}
}, [note]);
FYI, I am updating the state inside a TextInput, like so (I had to use onBlur to avoid the issue of react loosing focus on the first character type because I am passing a component within screenOptions of a TabNavigator):
<TextInput
key='note'
style={{
color: '#fff',
fontSize: 16,
width: '100%',
textAlign: 'center',
}}
placeholder='Tap here to share something...'
placeholderTextColor={styles.colors.textPlaceholder}
maxLength={50}
onBlur={(text) => setNote(text)}
defaultValue={note || props?.client?.note?.value}
/>
As I mentioned, this has been a tricky situation because I had to get around react's loss of focus when I rely on onChangeText() or onChange(). I am passing in a CustomHeader - which is a function inside the parent, to a TabNavigator within screenOptions like so:
screenOptions={{
headerShown: true,
headerStyle: {
backgroundColor: Colors.primary,
elevation: 0,
shadowOpacity: 0,
shadowColor: 'transparent',
height: 170,
},
key: 'patient-tab',
headerShadowVisible: false,
tabBarStyle: { backgroundColor: Colors.primary },
headerTintColor: Colors.light,
headerTitle: (props) => <CustomHeader {...props} />, // Passed in here
tabBarActiveTintColor: Colors.light,
tabBarInactiveTintColor: Colors.lightInactive,
tabBarActiveBackgroundColor: Colors.primary,
tabBarInactiveBackgroundColor: Colors.primary,
}}
The full code looks like this:
export const ClientBottomTabNavigator = (props) => {
const [note, setNote] = useState('');
const dispatch = useDispatch();
useEffect(() => {
if (props.client.id && note) {
console.log('updating note...');
dispatch(updateNote(props.client.id, note));
}
}, [props.client.id, note]);
const CustomHeader = () => {
return (
<View>
<View style={{ width: '100%', flexDirection: 'row', justifyContent: 'space-between', marginBottom: 6 }}>
<Feather
name='chevron-left'
size={24}
onPress={() => props.navigation.goBack()}
color={styles.colors.textInverse}
style={{ justifySelf: 'flex-start', alignSelf: 'flex-start', width: '32%', marginBottom: 6 }}
/>
<Text
style={{
color: '#fff',
fontSize: 18,
alignSelf: 'center',
justifySelf: 'center',
fontWeight: 'bold',
}}
>
{props?.client?.firstName} {props?.client?.lastName}
</Text>
<Feather
name='' // Leave this blank
style={{ justifySelf: 'flex-end', alignSelf: 'flex-end', width: '32%' }}
/>
</View>
<View style={{ alignItems: 'center', marginBottom: 6 }}>
<Text style={{ color: '#fff', fontSize: 18, marginBottom: 6 }}>
{convertDiscipline(props?.client?.discipline)}
</Text>
<View>
<TextInput
key='note'
style={{
color: '#fff',
fontSize: 16,
width: '100%',
textAlign: 'center',
}}
placeholder='Tap here to share something…’
placeholderTextColor={styles.colors.textPlaceholder}
maxLength={50}
onBlur={(text) => setNote(text)}
defaultValue={note || props?.client?.note?.value}
/>
</View>
</View>
</View>
);
};
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator
screenOptions={{
headerShown: true,
headerStyle: {
backgroundColor: Colors.primary,
elevation: 0,
shadowOpacity: 0,
shadowColor: 'transparent',
height: 170,
},
key: 'client-tab',
headerShadowVisible: false,
tabBarStyle: { backgroundColor: Colors.primary },
headerTintColor: Colors.light,
headerTitle: (props) => <CustomHeader {...props} />, // Passed in here
tabBarActiveTintColor: Colors.light,
tabBarInactiveTintColor: Colors.lightInactive,
tabBarActiveBackgroundColor: Colors.primary,
tabBarInactiveBackgroundColor: Colors.primary,
}}
initialRouteName={'Visits'}
>
<Tab.Screen
name='Visits'
component={VisitsTab}
options={{
tabBarLabel: 'VISITS',
tabBarIcon: ({ color, size }) => <FontAwesome name='street-view' color={color} size={size} />,
}}
/>
<Tab.Screen
name='Chart'
component={ChartTab}
options={{
tabBarLabel: 'CHARTS',
tabBarIcon: ({ color, size }) => <FontAwesome name='id-badge' color={color} size={size} />,
}}
/>
<Tab.Screen
name='Goals'
component={GoalsTab}
options={{
tabBarLabel: 'EDIT GOALS',
tabBarIcon: ({ color, size }) => <FontAwesome name='trophy' color={color} size={size} />,
}}
/>
</Tab.Navigator>
);
};
See here https://playcode.io/1106437, it works, but you have onBlur, you need to blur to trigger the action, maybe you need to use onChangeText insted.

React Native, Custom DrawerContent navigator issue

I have a custom DrawerContent component and I am unable to navigate to a screen from it using navigator.navigate('DrawerHelp').
I am still getting this error and I really have no idea how to fix it.
I am trying to navigate from the Help button to its own component called DrawerHelp.
issue The action 'NAVIGATE' with payload {"name":"DrawerHelp"} was not handled by any navigator
This is my code below.
function DrawerComponent() {
return (
<Drawer.Navigator
drawerContentOptions={{activeBackgroundColor: '#efefef', activeTintColor: '#000000'}}
initialRouteName="Tabs"
drawerStyle={{ backgroundColor:'#ffffff', width:'85%', paddingBottom: 50 }}
drawerContent={
props => ( <CustomDrawerContent {...props} /> )
}>
<Drawer.Screen name="Dashboard" component={Tabs} options={{
drawerIcon: config => <Ionicons name={'ios-home'} size={18} color={'#444'} headerTitle="AAA" />,
}} />
<Drawer.Screen name="Help" component={DrawerHelp}
options={{
drawerIcon: config => <Ionicons name={'ios-people-circle-outline'} size={18} color={'#444'}/>,
}}
/>
</Drawer.Navigator>
);
}
export function CustomDrawerContent (props) {
const [ tabs, setTabs ] = useState([
{
name: 'Help',
icon: 'ios-call',
borderColor: '#e7c53f',
backgroundColor: '#fff',
color: '#e7c53f',
fontWeight: '500'
},{
name: 'Share',
icon: 'ios-megaphone',
borderColor: '#e7c53f',
backgroundColor: '#fff',
color: '#e7c53f',
fontWeight: '500'
},{
name: 'Logout',
icon: 'ios-log-out',
borderColor: '#e7c53f',
backgroundColor: '#fff',
color: '#e7c53f',
fontWeight: '500'
}
]);
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
<View style={{ padding: 15 }}>
<View style={{
flexDirection: 'row',
justifyContent: 'space-between',
height: 50,
alignItems: 'center'
}}>
{
tabs.map((tab) => {
return (
<TouchableOpacity
key={tab.name}
style={{
height: '100%',
flex: .32,
alignItems: 'center',
borderWidth: .5,
borderColor: tab.borderColor,
backgroundColor: tab.backgroundColor,
borderRadius: 10,
flexDirection: 'row',
justifyContent: 'space-evenly'
}}
onPress={() => {
if(tab.name == 'Logout') {
// navigation.toggleDrawer();
}
if(tab.name == 'Share') {
// onShare();
}
if(tab.name == 'Help') {
props.navigation.navigate('DrawerHelp');
}
}}>
<Ionicons name={tab.icon} size={18} style={{ color: tab.color }} />
<Text style={{ color: tab.color, fontWeight: tab.fontWeight }}>{trans(tab.name, cntx.lang)}</Text>
</TouchableOpacity>
)
})
}
</View>
</View>
</DrawerContentScrollView>
);
}
It is not working because you have defined DrawerComponent with name 'Help'
<Drawer.Screen name="Help" component={DrawerHelp}
options={{drawerIcon: config => <Ionicons name={'ios-people-circle-outline'} size={18} color={'#444'}/>
if(tab.name == 'Help') {
props.navigation.navigate('DrawerHelp'); <== Change here to Help not DrawerHelp
}

How to access the nested navigator and how to setup properly the authentication flow

I'm quite new on react native this is just a practice project. now I have module regarding on authentication of users and how will navigate dashboard via conditioning the set asyncstorage item if the storage filtered that this storage has true value the user who access will directly move his/her screen to the AuthenticatedDriverScreen.
My Question:
How to navigate in a nested navigator?
How to call the getItem storage item on Navigator Screens?
My Error:
The action 'NAVIGATE' with payload {"name":"BindedScreenComponent","params":{"screen":"DriverDashboard"}} was not handled by any navigator.
Do you have a screen named 'BindedScreenComponent'?
I have already coded some functionality like Navigators Screen, Login Screen Etc. on the next paragraph I will share to you guys the code in every screens. I hope you will understand guys what I mean.
Navigator.JS
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
const Bind = createStackNavigator();
const UnauthenticatedScreen = () => {
return (
<Stack.Navigator>
<Stack.Screen
name="Login"
component={Login}
options=
{{
headerShown: false,
}}
/>
<Stack.Screen
name="Registration"
component={Register}
options={{
headerStyle: {
backgroundColor: '#4ABDFF'
},
headerTitleStyle: {
color: '#fff',
},
headerTintColor: '#fff',
}}
/>
<Stack.Screen
name="Privacy"
component={PrivacyPolicy}
options={{
headerStyle: {
backgroundColor: '#4ABDFF'
},
headerTitleStyle: {
color: '#fff',
},
headerTitle: 'Privacy Policy',
headerTintColor: '#fff',
}}
/>
<Stack.Screen
name="RegistrationSuccess"
component={RegistrationSuccess}
options=
{{
headerShown: false,
}}
/>
<Stack.Screen
name="ForgotPassword"
component={ForgotPassword}
options={{
headerStyle: {
backgroundColor: '#4ABDFF'
},
headerTitleStyle: {
color: '#fff',
},
headerTitle: 'Forgot Password',
headerTintColor: '#fff',
}}
/>
</Stack.Navigator>
)
}
const BindedScreenComponent = () => {
return (
<Bind.Navigator>
<Bind.Screen
name="DriverDashboard"
component={DriverDashboard}
options={{
headerStyle: {
backgroundColor: '#4ABDFF',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
headerTitle: 'Driver Dashboard'
}}
/>
<Bind.Screen
name="DriverProfile"
component={DriverProfile}
options={{
headerStyle: {
backgroundColor: '#4ABDFF'
},
headerTitleStyle: {
color: '#fff',
},
headerTitle: 'Profile',
headerTintColor: '#fff',
}}
/>
</Bind.Navigator>
)
}
const AuthenticatedDriverScreen = () => {
return (
<Drawer.Navigator drawerContent={props => <CustomDriverDrawer {...props} />} initialRouteName="DriverDashboard">
<Drawer.Screen
name="DriverDashboard"
component={BindedScreenComponent}
options={
{
drawerLabel: 'Home',
drawerIcon: ({ focused, color, size }) => (
<Icon type="font-awesome" name="home" style={{ fontSize: size, color: color }} />
),
}
}
/>
</Drawer.Navigator>
)
}
class Navigator extends React.Component {
constructor(props) {
super(props);
this.state = {
phone_number:'',
password:''
}
}
render() {
const isLogin = false
// try {
// const value = await AsyncStorage.getItem('access_token');
// if (value !== null) console.log(value)
// } catch (error) { // Error retrieving data }
return (
<NavigationContainer>
{isLogin ? <AuthenticatedDriverScreen /> : <UnauthenticatedScreen />}
</NavigationContainer>
)
}
}
export default Navigator;
Login.JS
const UserInfo = {phone_number:'09361876184', password: '123456'}
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
phone_number:'',
password:''
}
}
LoginSubmitFunction = async () => {
const phone_number = this.state.phone_number;
const password = this.state.password;
if(UserInfo.phone_number === phone_number && UserInfo.password === password) {
await AsyncStorage.setItem('userToken', '1');
this.props.navigation.navigate('BindedScreenComponent',{screen:'DriverDashboard'});
}else {
alert('Username or Password is Incorrect!');
}
}
render() {
return (
<SafeAreaView style={[styles.container, { backgroundColor: '#ffff' }]}>
<StatusBar barStyle="dark-content" backgroundColor="#ffff" />
<ScrollView>
<View style={{ marginTop: 60 }}>
<Text style={{ fontSize: 25, fontWeight: 'bold' }}>LOGO</Text>
<Text style={{ fontSize: 20, fontWeight: 'bold', marginTop: 30 }}>Login your credential</Text>
</View>
<View style={{ marginTop: 30 }}>
<TextInput
onChangeText={(phone_number) => this.setState({phone_number})}
value={this.state.phone_number}
placeholder="+63"
keyboardType='numeric'
style={{ height: 50, borderRadius: 5, paddingLeft: 20, borderColor: '#EEEEEE', borderWidth: 1, backgroundColor: '#EEEEEE' }}
/>
<TextInput
onChangeText={(password) => this.setState({password})}
value={this.state.password}
placeholder="Password"
secureTextEntry={true}
style={{ marginTop: 20, borderRadius: 5, paddingLeft: 20, height: 50, borderColor: '#EEEEEE', borderWidth: 1, backgroundColor: '#EEEEEE' }}
/>
</View>
<View style={{ marginTop: 30 }}>
<TouchableOpacity
onPress={this.LoginSubmitFunction}
style={{ height: 70 }} >
<Text style={{ backgroundColor: '#4ABDFF', fontSize: 20, textAlign: 'center', padding: 10, borderRadius: 5, color: '#ffff' }}>Login</Text>
</TouchableOpacity>
</View>
<View style={{ marginTop: 0, alignItems: 'center' }}>
<Text onPress={() => this.props.navigation.navigate('ForgotPassword')} style={{ fontSize: 17 }}>Forgot Password?</Text>
<Text style={{ fontSize: 17, marginTop: 10 }}>Don't have an account yet? <Text style={{ textDecorationLine: 'underline', color: '#4ABDFF', fontWeight: 'bold' }} onPress={() => this.props.navigation.navigate('Registration')}>Register Here</Text></Text>
</View>
</ScrollView>
</SafeAreaView>
)
}
}
const styles = StyleSheet.create({
container: { flex: 2, padding: 40 },
});
export default Login;
Output:
The action 'NAVIGATE' with payload {"name":"BindedScreenComponent","params":{"screen":"DriverDashboard"}} was not handled by any navigator.
Because your LoginScreen in UnauthenticatedScreen stack. But UnauthenticatedScreen not contain any screen has name "BindedScreenComponent".
With use isLogin variable to check login, You dont need use NAVIGATE.
await AsyncStorage.setItem('userToken', '1');
userToken is key, and has value is '1' in AsyncStorage.
In your Navigator.JS, You can call it:
const isLogin = await AsyncStorage.getItem('userToken') === '1'
if isLogin = true, it return AuthenticatedDriverScreen . And else, return UnauthenticatedScreen

React navigation Header

Im using react-native 0.62.0 with react-navigation version ^4.3.7 and createMaterialBottomTabNavigator. I have tried searching and implementing their solutions but none of it seems to work. I want to center the text in my header. This is what I tried.
static navigationOptions = ({ navigation }) => ({
title: 'Profile',
headerTitleStyle: { flex: 1, textAlign: 'center', color: 'white', fontSize: 20, alignSelf: 'center' , },
headerTintColor: 'white',
headerStyle: {
backgroundColor: '#4169E1',
},
})
Try with this:
static navigationOptions = ({ navigation }) => ({
title: 'Profile',
headerTitleStyle: { flex: 1, textAlign: 'center', color: 'white', fontSize: 20, alignSelf: 'center' , },
headerTintColor: 'white',
headerStyle: {
backgroundColor: '#4169E1',
},
headerTitleAlign: 'center',
})
Try after removing alignSelf: 'center' ,. The below code is working for me.
defaultNavigationOptions: ({ navigation }) => {
return {
headerStyle: {
backgroundColor: '#1e89f4'
},
headerTitle: 'Statistics',
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
textAlign: 'center',
flex: 1
},
headerLeft: (
<View style={{ paddingLeft: 13 }}>
<FontAwesomeIcon
size={25}
color='#fff'
icon={faBars}
onPress={() => navigation.openDrawer()}
/>
</View>
),
headerRight: <View />
};
}

React Native Navigation Custom Border

I am a beginner in React Native. I want to configure the React Navigation 5.x with custom style. And I am unable to trim border-bottom like following. Please help me with This.
Custom Style for React Navigation
My Current Code:
function StackNavigator() {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerStyle: styles.header,
headerBackImage: () => (
<Image
style={styles.headerBack}
source={require("../assets/icons/64x/chevron-left.png")}
/>
),
headerLeftContainerStyle: {
alignItems: "flex-start",
paddingHorizontal: theme.sizes.padding / 2
},
headerTitleStyle: styles.headerTitle
}}
>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{ headerShown: true }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
header: {
height: theme.sizes.base * 5,
backgroundColor: Colors.white,
borderWidth: 0,
elevation: 0,
borderBottomWidth: 1,
borderBottomColor: Colors.grayLight
},
headerBack: {
height: 20,
width: 20
},
headerTitle: {
fontSize: 18,
fontFamily: "Quicksand",
letterSpacing: -1
}
});
Thank you in advance.
You can add this style to the cardStyle property of the screenOptions:
function StackNavigator() {
<NavigationContainer>
<Stack.Navigator
screenOptions={{
cardStyle : { backgroundColor : 'lightgray', margin : 15 },
headerStyle: styles.header,
headerBackImage: () => (
<Image
style={styles.headerBack}
source={require("../assets/icons/64x/chevron-left.png")}
/>
),
headerLeftContainerStyle: {
alignItems: "flex-start",
paddingHorizontal: theme.sizes.padding / 2
},
headerTitleStyle: styles.headerTitle
}}
>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{ headerShown: true }}
/>
</Stack.Navigator>
</NavigationContainer>
}
Which will produce something like this:

Resources