如何使用 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