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
Related
I am using create Bottom Tab Navigator , version 6 , is there a way to pass navigation object from Tab.Screen to TabButton ? taking in account props of tabBarButton that I must also pass them to my custom component (TabButton) , here is my code thanks for your help :
<Tab.Screen key={index} name={item.route} component={item.component}
options={{
headerShown: false,
tabBarButton: (props) => <TabButton {...props} item={item} />
}}
/>
you can pass navigation and tabBarButton props in this way,
The options function receives the navigation prop and the route prop for that screen, so you may also use route there.
<Tab.Screen key={index} name={item.route} component={item.component}
options={({ navigation }) => ({
headerShown: false,
tabBarButton: (props) => <TabButton {...props} navigation={navigation} item={item} />
})
}
/>
Here's the example how you can do it
<Stack.Screen
name={"ComponentName"}
component={ComponentScreen}
options={(props) => {
return {
header: () => (
<CustomHeaderComponent
title={"SomeCoolTitle"}
{...props}
/>
),
};
}}
/>
I just added a global variable and affect to it props.navigation , like so :
return (
<Tab.Screen key={index} name={item.route} component={item.component}
options={( props ) => {
_navigation = props.navigation
return{
tabBarButton: (props) => <TabButton {...props} item={item} navigation={_navigation} />
}
}
}
/>
)
I keep getting the following error despite proving default values to my context.
TypeError: undefined is not an object (evaluating 'favoriteMealsCtx.ids')
Here's my context file:
import { createContext, useState } from "react";
export const FavoritesContext = createContext({
ids: [],
addFavorite: (id) => {
},
removeFavorite: (id) => {
}
});
function FavoritesContextProvider({children}) {
const [favoriteMealIds, setFavoriteMealIds] = useState([]);
function addFavorite(id) {
setFavoriteMealIds((currentFavIds) => [...currentFavIds, id]);
}
function removeFavorite(id) {
setFavoriteMealIds((currentFavIds) => currentFavIds.filter(mealId => mealId !== id))
}
const value = {
ids: favoriteMealIds,
addFavorite: addFavorite,
removeFavorite: removeFavorite
};
return (
<FavoritesContext.Provider value={value}>
{children}
</FavoritesContext.Provider>
)
}
export default FavoritesContextProvider;
Here's app.js using the <FavoritesContextProvider>:
export default function App() {
return (
<>
<StatusBar style="light"/>
<FavoritesContextProvider>
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerStyle: {backgroundColor: '#351401'},
headerTintColor: 'white',
contentStyle: {backgroundColor: '#3f2f25'},
}}
>
<Stack.Screen
name="Drawer"
component={DrawerNavigator}
options={{
headerShown: false,
}}
/>
<Stack.Screen name="MealsOverview" component={MealsOverviewScreen}/>
<Stack.Screen
name="MealDetail"
component={MealDetailScreen}
options={{
title: 'About the Meal',
}}
/>
</Stack.Navigator>
</NavigationContainer>
</FavoritesContextProvider>
</>
);
}
And finally here's the MealDetail screen throwing the error when it loads:
function MealDetailScreen({route, navigation}) {
const favoriteMealsCtx = useContext(FavoritesContext);
console.log(favoriteMealsCtx)
const mealId = route.params.mealId;
const mealIsFavorite = favoriteMealsCtx.ids.includes(mealId) // Here's the error
const selectedMeal = MEALS.find((meal) => meal.id === mealId);
function changeFavoriteStatusHandler() {
if ( mealIsFavorite ) {
favoriteMealsCtx.removeFavorite(mealId)
} else {
favoriteMealsCtx.addFavorite(mealId)
}
}
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => {
return (
<IconButton
icon={mealIsFavorite ? 'star' : 'star-outline'}
color="white"
onPress={changeFavoriteStatusHandler}
/>
);
},
});
}, [navigation, changeFavoriteStatusHandler]);
return (
<ScrollView style={styles.rootContainer}>
<Image style={styles.image} source={{uri: selectedMeal.imageUrl}}/>
<Text style={styles.title}>{selectedMeal.title}</Text>
<MealDetails
duration={selectedMeal.duration}
complexity={selectedMeal.complexity}
affordability={selectedMeal.affordability}
textStyle={styles.detailText}
/>
<View style={styles.listOuterContainer}>
<View style={styles.listContainer}>
<Subtitle>Ingredients</Subtitle>
<List data={selectedMeal.ingredients}/>
<Subtitle>Steps</Subtitle>
<List data={selectedMeal.steps}/>
</View>
</View>
</ScrollView>
);
}
I don't understand why const favoriteMealsCtx = useContext(FavoritesContext); is returning null when it should have been initialized already.
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>
);
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.
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>
)
}
};