如何使用 React Navigation v.5 中的 DrawerItemList 在抽屉导航器中传递自定义按钮?
How to pass a custom button in a drawer navigator, using the DrawerItemList in React Navigation v. 5?
我正在尝试迁移到 React Navigation 5,但在使用 DrawerItemList
代替 DrawerNavigatorItems
时出现错误。
我的代码曾经是这样的:
const MainNavigator = createDrawerNavigator(
// e.g.
Winners: {
screen: WinnersNavigator,
navigationOptions: {
drawerLabel: (
<BoldText style={NavigationStyles.arhiki}>Νικητές</BoldText>
),
drawerIcon: tabInfo => {
return (
<View style={NavigationStyles.winners}>
<FontAwesome
name="users"
size={iconMultiplier / 10}
color={tabInfo.tintColor}
/>
</View>
);
}
}
},
// ... more screens
contentComponent: props => {
const dispatch = useDispatch();
// This is for showing the Admin screen link, if user is an admin.
const userIdExists = useSelector(state => state.auth.userId);
return (
<View style={{ flex: 1 }}>
<SafeAreaView forceInset={{ top: "always", horizontal: "never" }}>
{/* These are the default drawer items */}
// THE PROBLEM IS HERE!!!
<DrawerNavigatorItems {...props} />
{/* Plus our custom buttons */}
{userIdExists && (
<View style={NavigationStyles.summary}>
<Ionicons.Button
name="ios-create"
backgroundColor={Colours.moccasin_light}
size={iconMultiplier / 10}
// style={{marginLeft: -20 }}
color="#888"
onPress={() => props.navigation.navigate("CreateWelcome")}
></Ionicons.Button>
<Text
onPress={() => props.navigation.navigate("CreateWelcome")}
style={[
NavigationStyles.exodos,
Platform.OS == "android" ? { marginLeft: -6 } : null
]}
>
Δημιουργία
</Text>
</View>
)}
...
现在看起来像这样:
const CustomDrawerContent = props => {
const dispatch = useDispatch();
// This is for showing the Admin screen link, if user is an admin.
const userIdExists = useSelector(state => state.auth.userId);
return (
<View style={{ flex: 1 }}>
<SafeAreaView forceInset={{ top: "always", horizontal: "never" }}>
<DrawerContentScrollView {...props}>
{/* These are the default drawer items */}
<DrawerItemList {...props} />
{/* Plus our custom buttons */}
<Drawer.Section>
{userIdExists && (
<View style={NavigationStyles.summary}>
<DrawerItem
label={() => (
<Text
// onPress={() => props.navigation.navigate("CreateWelcome")}
style={[
NavigationStyles.exodos,
Platform.OS == "android" ? { marginLeft: -6 } : null
]}
>
Δημιουργία
</Text>
)}
icon={() => (
<Ionicons.Button
name="ios-create"
backgroundColor={Colours.moccasin_light}
size={iconMultiplier / 10}
// style={{marginLeft: -20 }}
color="#888"
// onPress={() => props.navigation.navigate("CreateWelcome")}
></Ionicons.Button>
)}
onPress={() => props.navigation.navigate("CreateWelcome")}
/>
</View>
)}
// ... more items
</Drawer.Section>
</DrawerContentScrollView>
</SafeAreaView>
</View>
);
};
const MainDrawerNavigator = createDrawerNavigator();
export const MainNavigator = () => {
return (
<MainDrawerNavigator.Navigator
drawerStyle={{
width: width < 900 ? 0.6 * width : 0.4 * width,
backgroundColor: Colours.moccasin_light,
overlayColor: Colours.maroonRGBA
}}
drawerContentOptions={{ activeTintColor: Colours.gr_brown }}
drawerContent={props => <CustomDrawerContent {...props} />}
>
<MainDrawerNavigator.Screen
name="GameNavigator"
component={GameNavigator}
options={{
drawerLabel: (
<BoldText style={NavigationStyles.arhiki}>Αρχική</BoldText>
),
drawerIcon: ({ color }) => (
<View style={NavigationStyles.shield}>
<MaterialCommunityIcons
name="shield-cross"
size={iconMultiplier / 8}
color={color}
/>
</View>
)
}}
/>
// ... more screens
</MainDrawerNavigator.Navigator>
);
};
我得到的错误是:
TypeError: label is not a function. (In 'label({
color: color,
focused: focused
})', 'label' is an instance of Object)
它是在 <DrawerItemList {...props} />
处生成的
在 docs 我读到:
import {
DrawerContentScrollView,
DrawerItemList,
} from '@react-navigation/drawer';
function CustomDrawerContent(props) {
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
</DrawerContentScrollView>
);
}
// To add additional items in the drawer, you can use the DrawerItem component:
function CustomDrawerContent(props) {
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
<DrawerItem
label="Help"
onPress={() => Linking.openURL('https://mywebsite.com/help')}
/>
</DrawerContentScrollView>
);
}
我渲染的第一个屏幕是导航器 GameNavigator
。
这会是个问题吗?
我在一期中读到:
“无法在抽屉内容中添加导航器。您可以使用自定义路由器和自定义导航器实现自定义布局:” source,但我已经从课程中了解到这是可能的!还是这家伙 抽屉内容 有别的意思?
GameNavigator 是:
const GameStackNavigator = createStackNavigator();
const GameNavigator = () => {
return (
<GameStackNavigator.Navigator
initialRouteName="Welcome"
screenOptions={defaultNavOptions}
>
<GameStackNavigator.Screen
name="Welcome"
component={WelcomeScreen}
options={WelcomeScreenOptions}
/>
...
</GameStackNavigator.Navigator>
);
};
而 WelcomeScreen
是:
const WelcomeScreen = props => {
const dispatch = useDispatch();
const [isLoading, setIsLoading] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
const [gridTileAnimValue] = useState(new Animated.Value(0));
const [isConnected, setIsConnected] = useState(false);
// For adding the points that are saved in memory, when connection is established.
useEffect(() => {
const unsub = NetInfo.addEventListener(state => {
setIsConnected(state.isConnected);
});
return () => unsub();
}, []);
const getPoints = async () => {
let points = await AsyncStorage.getItem("savedPoints");
if (!!points) {
const getEmail = async () => {
const userData = await AsyncStorage.getItem("userData");
if (userData) {
const transformedData = JSON.parse(userData);
const { userEmail } = transformedData;
return userEmail;
}
};
const email = await getEmail();
// Give it some time to get the token and userId,
// because saveData needs them.
setTimeout(
async () => await dispatch(dataActions.saveData(email, +points)),
3000
);
await AsyncStorage.removeItem("savedPoints");
}
};
if (isConnected) getPoints();
useEffect(() => {
getFilters = async () => {
await dispatch(filtersActions.fetchDifficultyLevelFilters());
await dispatch(filtersActions.fetchCategoriesFilters());
};
}, [dispatch]);
useEffect(() => {
props.navigation.setOptions({
headerRight: () => (
<HeaderButtons HeaderButtonComponent={CustomHeaderButton}>
<Item
title="game-info"
iconName={
Platform.OS === "android"
? "md-information-circle-outline"
: "ios-information-circle-outline"
}
// style={{width: width / 8, height: height / 10, paddingTop: height / 35}}
onPress={() => setModalVisible(!modalVisible)}
/>
</HeaderButtons>
)
});
}, [modalVisible, setModalVisible]);
useEffect(() => {
const checkIfInfoNeeded = async () => {
return await AsyncStorage.getItem("NO_infoNeeded");
};
checkIfInfoNeeded().then(NO_infoNeeded => {
if (NO_infoNeeded === "NO") {
return;
} else {
setModalVisible(true);
}
});
}, []);
const animateGridTile = () => {
Animated.timing(gridTileAnimValue, {
toValue: 1,
duration: 1200,
useNativeDriver: false
}).start();
};
useEffect(() => {
animateGridTile();
const unsubscribe = props.navigation.addListener("focus", animateGridTile);
return () => unsubscribe();
}, [animateGridTile]);
useEffect(() => {
const unsubscribe = props.navigation.addListener("willBlur", () =>
gridTileAnimValue.setValue(0)
);
return () => {
unsubscribe();
};
}, []);
const cardStyle = { opacity: gridTileAnimValue };
const renderGridItem = itemData => {
return (
<Animated.View style={cardStyle}>
<CategoryGridTile
color={itemData.item.color}
title={itemData.item.title}
id={itemData.item.id}
onSelect={() => {
if (itemData.item.id == 0) {
props.navigation.navigate({
routeName: "MixedChoicesScreen"
});
} else if (itemData.item.id == 1) {
props.navigation.navigate({
routeName: "MultiChoiceCategories",
params: {
gameType: itemData.item.title
}
});
} else if (itemData.item.id == 2) {
props.navigation.navigate({
routeName: "TrueFalseCategories",
params: {
gameType: itemData.item.title
}
});
}
}}
/>
</Animated.View>
);
};
if (isLoading) {
return (
<CustomLinearGradient>
<View style={styles.centered}>
<ActivityIndicator size="large" color={Colours.moccasin_light} />
</View>
</CustomLinearGradient>
);
}
return (
<CustomLinearGradient>
<View style={styles.flatListContainer}>
{modalVisible && (
<CustomModal
modalVisible={modalVisible}
setModalVisible={setModalVisible}
onRequestClose={() => {
Alert.alert(
"Επιλογές",
"Παρακαλώ επιλέξτε μία από τις δύο επιλογές της καρτούλας: Ναι ή Όχι.",
[{ text: "Εντάξει", style: "default" }]
);
// Alert.alert("Παρακαλώ επιλέξτε μία από τις δύο επιλογές της καρτούλας: Πληροφορίες ή Δεν χρειάζεται.");
}}
textOne="Θέλετε να διαβάσετε τις οδηγίες χρήσεως και τις πληροφορίες σχετικά με τις
δοκιμαστικές εκδόσεις της εφαρμογής."
buttonOneTitle="Ναι"
buttonTwoTitle="Όχι"
onPressOne={async () => {
AsyncStorage.setItem("NO_infoNeeded", "NO");
setModalVisible(false);
props.navigation.navigate("GameInfo");
}}
onPressTwo={async () => {
AsyncStorage.setItem("NO_infoNeeded", "NO");
setModalVisible(false);
}}
/>
)}
<FlatList
// numColumns={2}
keyExtractor={(item, index) => item.id}
data={GAME_TYPES}
renderItem={renderGridItem}
/>
</View>
</CustomLinearGradient>
);
};
export const WelcomeScreenOptions = ({ route, navigation }) => {
return {
title: "ΕΝ ΤΟΥΤΩ ΝΙΚΑ",
headerLeft: () => (
<HeaderButtons HeaderButtonComponent={CustomHeaderButton}>
<Item
onPress={() => navigation.toggleDrawer()}
title="Menu"
iconSize={73}
iconName={Platform.OS === "android" ? "md-menu" : "ios-menu"}
// style={{
// width: width / 8,
// height: height / 10,
// paddingTop: height / 35
// }}
/>
</HeaderButtons>
)
};
};
如有任何建议,我们将不胜感激。
谢谢
对于drawerLabel你直接设置了一个对象是错误的
drawerLabel: (
<BoldText style={NavigationStyles.arhiki}>Αρχική</BoldText>
),
这应该是一个字符串或一个 returns 组件的函数,因此您应该像下面这样更改它
drawerLabel: {()=>(
<BoldText style={NavigationStyles.arhiki}>Αρχική</BoldText>
)},
你可以参考文档
https://reactnavigation.org/docs/drawer-navigator/#drawerlabel
我正在尝试迁移到 React Navigation 5,但在使用 DrawerItemList
代替 DrawerNavigatorItems
时出现错误。
我的代码曾经是这样的:
const MainNavigator = createDrawerNavigator(
// e.g.
Winners: {
screen: WinnersNavigator,
navigationOptions: {
drawerLabel: (
<BoldText style={NavigationStyles.arhiki}>Νικητές</BoldText>
),
drawerIcon: tabInfo => {
return (
<View style={NavigationStyles.winners}>
<FontAwesome
name="users"
size={iconMultiplier / 10}
color={tabInfo.tintColor}
/>
</View>
);
}
}
},
// ... more screens
contentComponent: props => {
const dispatch = useDispatch();
// This is for showing the Admin screen link, if user is an admin.
const userIdExists = useSelector(state => state.auth.userId);
return (
<View style={{ flex: 1 }}>
<SafeAreaView forceInset={{ top: "always", horizontal: "never" }}>
{/* These are the default drawer items */}
// THE PROBLEM IS HERE!!!
<DrawerNavigatorItems {...props} />
{/* Plus our custom buttons */}
{userIdExists && (
<View style={NavigationStyles.summary}>
<Ionicons.Button
name="ios-create"
backgroundColor={Colours.moccasin_light}
size={iconMultiplier / 10}
// style={{marginLeft: -20 }}
color="#888"
onPress={() => props.navigation.navigate("CreateWelcome")}
></Ionicons.Button>
<Text
onPress={() => props.navigation.navigate("CreateWelcome")}
style={[
NavigationStyles.exodos,
Platform.OS == "android" ? { marginLeft: -6 } : null
]}
>
Δημιουργία
</Text>
</View>
)}
...
现在看起来像这样:
const CustomDrawerContent = props => {
const dispatch = useDispatch();
// This is for showing the Admin screen link, if user is an admin.
const userIdExists = useSelector(state => state.auth.userId);
return (
<View style={{ flex: 1 }}>
<SafeAreaView forceInset={{ top: "always", horizontal: "never" }}>
<DrawerContentScrollView {...props}>
{/* These are the default drawer items */}
<DrawerItemList {...props} />
{/* Plus our custom buttons */}
<Drawer.Section>
{userIdExists && (
<View style={NavigationStyles.summary}>
<DrawerItem
label={() => (
<Text
// onPress={() => props.navigation.navigate("CreateWelcome")}
style={[
NavigationStyles.exodos,
Platform.OS == "android" ? { marginLeft: -6 } : null
]}
>
Δημιουργία
</Text>
)}
icon={() => (
<Ionicons.Button
name="ios-create"
backgroundColor={Colours.moccasin_light}
size={iconMultiplier / 10}
// style={{marginLeft: -20 }}
color="#888"
// onPress={() => props.navigation.navigate("CreateWelcome")}
></Ionicons.Button>
)}
onPress={() => props.navigation.navigate("CreateWelcome")}
/>
</View>
)}
// ... more items
</Drawer.Section>
</DrawerContentScrollView>
</SafeAreaView>
</View>
);
};
const MainDrawerNavigator = createDrawerNavigator();
export const MainNavigator = () => {
return (
<MainDrawerNavigator.Navigator
drawerStyle={{
width: width < 900 ? 0.6 * width : 0.4 * width,
backgroundColor: Colours.moccasin_light,
overlayColor: Colours.maroonRGBA
}}
drawerContentOptions={{ activeTintColor: Colours.gr_brown }}
drawerContent={props => <CustomDrawerContent {...props} />}
>
<MainDrawerNavigator.Screen
name="GameNavigator"
component={GameNavigator}
options={{
drawerLabel: (
<BoldText style={NavigationStyles.arhiki}>Αρχική</BoldText>
),
drawerIcon: ({ color }) => (
<View style={NavigationStyles.shield}>
<MaterialCommunityIcons
name="shield-cross"
size={iconMultiplier / 8}
color={color}
/>
</View>
)
}}
/>
// ... more screens
</MainDrawerNavigator.Navigator>
);
};
我得到的错误是:
TypeError: label is not a function. (In 'label({
color: color,
focused: focused
})', 'label' is an instance of Object)
它是在 <DrawerItemList {...props} />
在 docs 我读到:
import {
DrawerContentScrollView,
DrawerItemList,
} from '@react-navigation/drawer';
function CustomDrawerContent(props) {
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
</DrawerContentScrollView>
);
}
// To add additional items in the drawer, you can use the DrawerItem component:
function CustomDrawerContent(props) {
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
<DrawerItem
label="Help"
onPress={() => Linking.openURL('https://mywebsite.com/help')}
/>
</DrawerContentScrollView>
);
}
我渲染的第一个屏幕是导航器 GameNavigator
。
这会是个问题吗?
我在一期中读到:
“无法在抽屉内容中添加导航器。您可以使用自定义路由器和自定义导航器实现自定义布局:” source,但我已经从课程中了解到这是可能的!还是这家伙 抽屉内容 有别的意思?
GameNavigator 是:
const GameStackNavigator = createStackNavigator();
const GameNavigator = () => {
return (
<GameStackNavigator.Navigator
initialRouteName="Welcome"
screenOptions={defaultNavOptions}
>
<GameStackNavigator.Screen
name="Welcome"
component={WelcomeScreen}
options={WelcomeScreenOptions}
/>
...
</GameStackNavigator.Navigator>
);
};
而 WelcomeScreen
是:
const WelcomeScreen = props => {
const dispatch = useDispatch();
const [isLoading, setIsLoading] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
const [gridTileAnimValue] = useState(new Animated.Value(0));
const [isConnected, setIsConnected] = useState(false);
// For adding the points that are saved in memory, when connection is established.
useEffect(() => {
const unsub = NetInfo.addEventListener(state => {
setIsConnected(state.isConnected);
});
return () => unsub();
}, []);
const getPoints = async () => {
let points = await AsyncStorage.getItem("savedPoints");
if (!!points) {
const getEmail = async () => {
const userData = await AsyncStorage.getItem("userData");
if (userData) {
const transformedData = JSON.parse(userData);
const { userEmail } = transformedData;
return userEmail;
}
};
const email = await getEmail();
// Give it some time to get the token and userId,
// because saveData needs them.
setTimeout(
async () => await dispatch(dataActions.saveData(email, +points)),
3000
);
await AsyncStorage.removeItem("savedPoints");
}
};
if (isConnected) getPoints();
useEffect(() => {
getFilters = async () => {
await dispatch(filtersActions.fetchDifficultyLevelFilters());
await dispatch(filtersActions.fetchCategoriesFilters());
};
}, [dispatch]);
useEffect(() => {
props.navigation.setOptions({
headerRight: () => (
<HeaderButtons HeaderButtonComponent={CustomHeaderButton}>
<Item
title="game-info"
iconName={
Platform.OS === "android"
? "md-information-circle-outline"
: "ios-information-circle-outline"
}
// style={{width: width / 8, height: height / 10, paddingTop: height / 35}}
onPress={() => setModalVisible(!modalVisible)}
/>
</HeaderButtons>
)
});
}, [modalVisible, setModalVisible]);
useEffect(() => {
const checkIfInfoNeeded = async () => {
return await AsyncStorage.getItem("NO_infoNeeded");
};
checkIfInfoNeeded().then(NO_infoNeeded => {
if (NO_infoNeeded === "NO") {
return;
} else {
setModalVisible(true);
}
});
}, []);
const animateGridTile = () => {
Animated.timing(gridTileAnimValue, {
toValue: 1,
duration: 1200,
useNativeDriver: false
}).start();
};
useEffect(() => {
animateGridTile();
const unsubscribe = props.navigation.addListener("focus", animateGridTile);
return () => unsubscribe();
}, [animateGridTile]);
useEffect(() => {
const unsubscribe = props.navigation.addListener("willBlur", () =>
gridTileAnimValue.setValue(0)
);
return () => {
unsubscribe();
};
}, []);
const cardStyle = { opacity: gridTileAnimValue };
const renderGridItem = itemData => {
return (
<Animated.View style={cardStyle}>
<CategoryGridTile
color={itemData.item.color}
title={itemData.item.title}
id={itemData.item.id}
onSelect={() => {
if (itemData.item.id == 0) {
props.navigation.navigate({
routeName: "MixedChoicesScreen"
});
} else if (itemData.item.id == 1) {
props.navigation.navigate({
routeName: "MultiChoiceCategories",
params: {
gameType: itemData.item.title
}
});
} else if (itemData.item.id == 2) {
props.navigation.navigate({
routeName: "TrueFalseCategories",
params: {
gameType: itemData.item.title
}
});
}
}}
/>
</Animated.View>
);
};
if (isLoading) {
return (
<CustomLinearGradient>
<View style={styles.centered}>
<ActivityIndicator size="large" color={Colours.moccasin_light} />
</View>
</CustomLinearGradient>
);
}
return (
<CustomLinearGradient>
<View style={styles.flatListContainer}>
{modalVisible && (
<CustomModal
modalVisible={modalVisible}
setModalVisible={setModalVisible}
onRequestClose={() => {
Alert.alert(
"Επιλογές",
"Παρακαλώ επιλέξτε μία από τις δύο επιλογές της καρτούλας: Ναι ή Όχι.",
[{ text: "Εντάξει", style: "default" }]
);
// Alert.alert("Παρακαλώ επιλέξτε μία από τις δύο επιλογές της καρτούλας: Πληροφορίες ή Δεν χρειάζεται.");
}}
textOne="Θέλετε να διαβάσετε τις οδηγίες χρήσεως και τις πληροφορίες σχετικά με τις
δοκιμαστικές εκδόσεις της εφαρμογής."
buttonOneTitle="Ναι"
buttonTwoTitle="Όχι"
onPressOne={async () => {
AsyncStorage.setItem("NO_infoNeeded", "NO");
setModalVisible(false);
props.navigation.navigate("GameInfo");
}}
onPressTwo={async () => {
AsyncStorage.setItem("NO_infoNeeded", "NO");
setModalVisible(false);
}}
/>
)}
<FlatList
// numColumns={2}
keyExtractor={(item, index) => item.id}
data={GAME_TYPES}
renderItem={renderGridItem}
/>
</View>
</CustomLinearGradient>
);
};
export const WelcomeScreenOptions = ({ route, navigation }) => {
return {
title: "ΕΝ ΤΟΥΤΩ ΝΙΚΑ",
headerLeft: () => (
<HeaderButtons HeaderButtonComponent={CustomHeaderButton}>
<Item
onPress={() => navigation.toggleDrawer()}
title="Menu"
iconSize={73}
iconName={Platform.OS === "android" ? "md-menu" : "ios-menu"}
// style={{
// width: width / 8,
// height: height / 10,
// paddingTop: height / 35
// }}
/>
</HeaderButtons>
)
};
};
如有任何建议,我们将不胜感激。
谢谢
对于drawerLabel你直接设置了一个对象是错误的
drawerLabel: (
<BoldText style={NavigationStyles.arhiki}>Αρχική</BoldText>
),
这应该是一个字符串或一个 returns 组件的函数,因此您应该像下面这样更改它
drawerLabel: {()=>(
<BoldText style={NavigationStyles.arhiki}>Αρχική</BoldText>
)},
你可以参考文档 https://reactnavigation.org/docs/drawer-navigator/#drawerlabel