Can´t use navigation && props in React-Native? - reactjs

I am working on a React-Native Project and found an issue..
Everything is working fine except my HomeStackScreen, i want to pass data via props, but also want to navigate to open a drawer
const HomeStackScreen = (props, navigation) => (
console.log("HOME STACK: " + props.studentData),
<HomeStack.Navigator headerMode="screen" studentData={props.studentData}>
<HomeStack.Screen name = "Home" children={() => <HomeScreen studentData={props.studentData} />} options={{
title:'Home',
headerStyle: {
backgroundColor: '#e67e22',
},
headerRight: () => (
<Icon.Button name="build" size={30}
backgroundColor="#e67e22" color="white" paddingLeft= {15} onPress= {() => navigation.
openDrawer()}></Icon.Button>
)
}}/>
</HomeStack.Navigator>
);
Here is my code, but when I write
const HomeStackScreen = (props) => (
or
const HomeStackScreen = ({navigation}) => (
everything works, but when I want to use both I get an error, telling me one of them is not a function

You should use them like this :
const HomeStackScreen = (props) => {
const {navigation,studentData} = props ;
return ...
}

Pass props as an object and:
const HomeStackScreen = ({navigation, studentData}) => (
console.log("HOME STACK: " + studentData),
<HomeStack.Navigator headerMode="screen" >
<HomeStack.Screen name = "Home" children={() => <HomeScreen studentData={studentData} />} options={{
title:'Home',
headerStyle: {
backgroundColor: '#e67e22',
},
headerRight: () => (
<Icon.Button name="build" size={30}
backgroundColor="#e67e22" color="white" paddingLeft= {15} onPress= {() => navigation.
openDrawer()}></Icon.Button>
)
}}/>
</HomeStack.Navigator>
);

Related

How to render the header again in react navigation when user add something

Here
Let me show you the photo.
What I want to do is I want to add number as badge beside the shopping cart.
But the problem is that the navigation component does not re-render even when I change the value from another component.
here the code for header navigator
const createMyStackNavigator = (name, screen, whatStack) => ({navigation}) => {
whatStack = createStackNavigator();
return (
<whatStack.Navigator
screenOptions={{
headerStyle: {backgroundColor: `${headerColor}`},
headerTintColor: '#fff',
}}>
<whatStack.Screen
name={name}
component={screen}
options={{
title: name,
headerLeft: () => (
<Icon2.Button
name="navicon"
size={25}
backgroundColor={headerColor}
onPress={() => {
navigation.openDrawer();
}}></Icon2.Button>
),
headerRight: () => (
<Icon2.Button
name="shopping-bag"
size={25}
backgroundColor={headerColor}
onPress={() => {
navigation.openDrawer();
}}>
<Text style={{color: 'white'}}>{productInCart}</Text>
</Icon2.Button>
),
}}
/>
</whatStack.Navigator>
);
};
Here is how it creating the navigator
const DrawerNavigator = ({navigation}) => {
const isFocused = useIsFocused();
const cartList = useSelector((state) => state.productReducer.cartList);
productInCart = cartList.OrderDTO?.OrderLines.length
return (
<Drawer.Navigator drawerContent={(props) => <DrawerContent {...props} />}>
<Drawer.Screen
name="ProductScreen"
component={ProductScreenStackScreen}
/>
</Drawer.Navigator>
);
};
As you can see {productinCart} should be updated when its value is updated in other component.
But It's does not. Can you guys suggest some ideas? So that whenever user click certain button
The header will renader again

Hide TopTabNavigator inside StackNavigator

I am using createMaterialTopTabNavigator from react-navigation and each Tab has StackNavigator. How can I hide Top tabs after opening Stack screen? Currently using Expo managed with Redux.
Here's the code:
const favStack = createStackNavigator();
const loserStack = createStackNavigator();
const gainerStack = createStackNavigator();
const activeStack = createStackNavigator();
const Tab = createBottomTabNavigator();
const MarketsTab = createMaterialTopTabNavigator();
export default class App extends Component {
state = { loading: true };
favStackScreen = () => (
<favStack.Navigator>
<favStack.Screen name="Favorites" component={Favorites} />
<favStack.Screen name="Stock" component={Stock} />
</favStack.Navigator>
);
loserStackScreen = () => (
<loserStack.Navigator tabBarVisible="false">
<loserStack.Screen name="Losers" component={Losers} />
<loserStack.Screen name="Stock" component={Stock} />
</loserStack.Navigator>
);
gainerStackcreen = () => (
<gainerStack.Navigator headerMode="float">
<gainerStack.Screen name="Gainers" component={Gainers} />
<gainerStack.Screen name="Stock" component={Stock} />
</gainerStack.Navigator>
);
activeStackScreen = () => (
<activeStack.Navigator>
<activeStack.Screen name="MostActive" component={MostActive} />
<activeStack.Screen name="Stock" component={Stock} />
</activeStack.Navigator>
);
MarketsTabScreen = () => (
<MarketsTab.Navigator
style={{
paddingTop:
Platform.OS === "ios"
? Constants.statusBarHeight
: StatusBar.currentHeight,
}}
>
<MarketsTab.Screen
name="Losers"
component={this.loserStackScreen}
></MarketsTab.Screen>
<MarketsTab.Screen
name="Gainers"
component={this.gainerStackcreen}
></MarketsTab.Screen>
<MarketsTab.Screen
name="MostActive"
component={this.activeStackScreen}
></MarketsTab.Screen>
</MarketsTab.Navigator>
);
async componentDidMount() {
await Font.loadAsync({
Roboto: require("native-base/Fonts/Roboto.ttf"),
Roboto_medium: require("native-base/Fonts/Roboto_medium.ttf"),
...Ionicons.font,
});
this.setState({ loading: false });
}
render() {
if (this.state.loading) {
return <AppLoading style={styles.container} />;
}
return (
<Provider store={store.store}>
<PersistGate loading={null} persistor={store.persistor}>
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Favorites" component={this.favStackScreen} />
<Tab.Screen name="Markets" component={this.MarketsTabScreen} />
</Tab.Navigator>
</NavigationContainer>
</PersistGate>
</Provider>
);
}
}
For managing state I am using Redux and all my components are class(so can not use Hooks).
Components being used: (Gainers, Losers, MostActive as Top Tabs) and Favorites as Bottom Tabs. All of them should have access to Stock component.
In your case you can set the tabBar prop of your MarketsTab navigator to a function that returns null like this:
MarketsTabScreen = () => (
<MarketsTab.Navigator
tabBar={() => null}
style={{
paddingTop:
Platform.OS === 'ios'
? Constants.statusBarHeight
: StatusBar.currentHeight,
}}>
<MarketsTab.Screen name="Losers" component={this.loserStackScreen} />
<MarketsTab.Screen name="Gainers" component={this.gainerStackcreen} />
<MarketsTab.Screen name="MostActive" component={this.activeStackScreen} />
</MarketsTab.Navigator>
);
You can still swipe to the screens in the tab navigator, but the top tab bar won't be shown.

React Navigation 5: How can I put route along with other options

I am trying to pass navigation params inside my navigation.js:
import { CATEGORIES, MEALS } from '../data/dummy-data';
<MealsNav.Screen
name="MealDetail"
component={MealDetailScreen}
options={({ route }) => {
const mealId = route.params.mealId;
const selectedMeal = MEAL.find(meal => meal.id === mealId);
return {
title: selectedMeal.title
},
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Favorite'
iconName='ios-menu'
onPress={() => console.log('Mark as the favorite')}
/>
</HeaderButtons>
),
}}
/>
This one doesn't work and I am not seeing the headerRight() being highlighted.
This part works though:
<MealsNav.Screen
name="CategoryMeals"
component={CategoryMealsScreen}
options={({ route }) => {
const catId = route.params.categoryId;
const selectedCategory = CATEGORIES.find((cat) => cat.id === catId);
return {
title: selectedCategory.title,
};
}}
/>
I just need the route + the other options to sit together.
The error says: error: Error: Unexpected token, expected ";" (92:31)
And the headerRight function did not executed since the icon did not shows up.
headerRight: () => (
What am I doing wrong here?
standart solution
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={({ navigation, route }) => ({
headerTitle: props => <LogoTitle {...props} />,
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#000"
/>
),
})
}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Be carefully after return function called
because nothing works after return called

Exposing state props from functional component to navigationOptions function

I have a component that renders the input field, I want to pass the data to the next page when user clicks on "next" button in the header. What is the best practice for doing so? How do I expose this into Page.navigationOptions?
Or is it best to just set up redux for these types of things?
const Page = () => {
const [desc, getDesc] = useState('');
return (
<View style={styles.inputFieldDescContainer}>
<TextInput
multiline
placeholder='Write a description...'
onChangeText={(text) => getDesc(text)}
value={desc}
/>
</View>
);
};
// How do I pass desc properties down into navigationOptions?
Page.navigationOptions = (navData) => {
return {
headerTitle: 'Page,
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Next'
onPress={() => {
navData.navigation.navigate('NextPage', {data: navData});
}}
/>
</HeaderButtons>
),
headerBackTitle: null
};
};
/* NextPage.js */
const NextPage = (props) => {
console.log('enter props data', props.navigation.getParam('data'));
console.log('enter props navigation', props.navigation);
const [valueText, setValueText] = useState();
return (
<View>
<TextInput onChangeText={(text) => setValueText(text)} value={valueText}/>
<TouchableOpacity><Text>Create your workout</Text></TouchableOpacity>
</View>
);
;}
Sharing state and props between component and options is possible in React Navigation 5 https://blog.expo.io/announcing-react-navigation-5-0-bd9e5d45569e
In React Navigation 4, you can use params to store the value to be able to share it:
const Page = ({ navigation }) => {
const desc = navigation.getParam('description', '');
return (
<View style={styles.inputFieldDescContainer}>
<TextInput
multiline
placeholder='Write a description...'
onChangeText={(text) => navigation.setParams({ description: text )}
value={desc}
/>
</View>
);
}

React Native searchbar with react-navigation

I would like to add a searchbar in my header. I am using react-navigation, and want to create an effect like in the below 2 pictures. When you press the search icon, the hamburger icon becomes a arrow-back icon, the header title becomes a search field. I have tried to accomplish this with the navigationOptions but the problem is that it is static and it cannot be updated when an action happens on the header itself. So for experimenting what I want to accomplish is that when the search icon is pressed, the hamburger icon becomes a arrow-back icon. Thank you!
var search = false;
const menuButton = navData => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName="ios-menu"
onPress={() => {
navData.navigation.toggleDrawer();
}}
/>
</HeaderButtons>
);
const goBackButton = navData => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName="ios-arrow-back"
onPress={() => {
search=false
}}
/>
</HeaderButtons>
);
MyScreen.navigationOptions = navData => {
return {
headerTitle: 'My title',
headerLeft: search ? goBackButton : menuButton(navData),
headerRight: (
<BorderlessButton
onPress={() => search=true}
style={{ marginRight: 15 }}>
<Ionicons
name="md-search"
size={Platform.OS === 'ios' ? 22 : 25}
/>
</BorderlessButton>
)
}
};
try use state bro !
constructor(props) {
super(props);
this.state = {
search:false
}
}
const menuButton = navData => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName="ios-menu"
onPress={() => {
navData.navigation.toggleDrawer();
}}
/>
</HeaderButtons>
);
const goBackButton = navData => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName="ios-arrow-back"
onPress={() => {this.setState({search:false})}}
/>
</HeaderButtons>
);
MyScreen.navigationOptions = navData => {
return {
headerTitle: 'My title',
headerLeft: this.state.search ? goBackButton : menuButton(navData),
headerRight: (
<BorderlessButton
onPress={() => {this.setState({search:true})}}
style={{ marginRight: 15 }}>
<Ionicons
name="md-search"
size={Platform.OS === 'ios' ? 22 : 25}
/>
</BorderlessButton>
)
}
};
I have fixed it by passing information to my navigationOptions with setParams and getParam. The only problem I faced was in infinite loop which I could solve with the proper use of useEffect and useCallback.
const MyScreen = props => {
const dispatch = useDispatch();
var search = useSelector(state => state.verbs.search);
useEffect(() => {
props.navigation.setParams({search: search});
}, [search]);
const toggleSearchHandler = useCallback(() => {
dispatch(toggleSearch());
}, [dispatch]);
useEffect(() => {
props.navigation.setParams({toggleSearch: toggleSearchHandler});
}, [toggleSearchHandler]);
return (<View> ...
</View>
);
};
MyScreen.navigationOptions = navData => {
const toggleSearch = navData.navigation.getParam('toggleSearch')
return {
headerTitle: 'Verbs',
headerLeft: navData.navigation.getParam('search') ? gobackButton : menuButton(navData),
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName="ios-star"
onPress={toggleSearch}
/>
</HeaderButtons>
)
}
};

Resources