So, I was getting familiar with reanimated2, for that I'm building a simple todo app, so whenever I check a single todo (a simple toggle state, completed or !completed), I change the text style to line-through so that it appears to be completed, now in order to add some animations, I was trying to combine reanimated to it, but was unsuccessful, so can anyone guide me how to achieve what I want?
const TodoItem = ({ todo }) => {
const { toggleTodo } = useTodoStore((state) => state);
const progress = useSharedValue('none');
useEffect(() => {
progress.value = withTiming(todo.completed ? 'line-through' : 'none', {
duration: 1000,
easing: Easing.linear,
})
}, [todo.completed]);
const textReanimatedStyle = useAnimatedStyle(() => ({
textDecorationLine: progress.value, // ts error: Type 'string' is not assignable to type 'number | "none" | "underline" | "line-through" | "underline line-through" | undefined'.
}), []);
const toggleTodoHandler = () => {
toggleTodo(todo.id);
};
return (
<Animated.View
style={styles.container}
entering={FadeIn}
exiting={FadeOut}
layout={Layout.delay(300)}>
<View style={styles.innerContainer}>
<TouchableOpacity
style={styles.checkboxContainer}
activeOpacity={0.6}
onPress={toggleTodoHandler}>
{todo.completed ? (
<Ionicon name="checkmark" size={20} color={'gray'} />
) : null}
</TouchableOpacity>
<Animated.Text
numberOfLines={2}
style={[
styles.text,
textReanimatedStyle,
]}>
{todo.text}
</Animated.Text>
</View>
</Animated.View>
);
};
Now whenever I toggle complete or not, it throws error saying:
Invalid RCTTextDecorationLineType 'noneNaN'. should be one of: (
"line-through",
none,
underline,
"underline line-through"
)
Not sure this is what you wanted to achive. But what I did was can be handle with a state change also without reanimated.
There is an alternate way also. check this answer https://stackoverflow.com/a/65262789/8743951
const textState = useSharedValue('none');
const [todo, setTodo] = useState<{ text: string; completed: boolean }>({ text: 'complete this', completed: false });
const textReanimatedStyle = useAnimatedStyle(() => {
if (textState.value === 'checked') {
return {
textDecorationLine: 'line-through',
};
}
return {
textDecorationLine: 'none',
};
}, []);
const toggleTodoHandler = (): void => {
textState.value = todo.completed ? 'none' : 'checked';
setTodo({ ...todo, completed: !todo.completed });
};
return (
<Animated.View style={styles.container} entering={FadeIn} exiting={FadeOut} layout={Layout.delay(300)}>
<View style={styles.innerContainer}>
<TouchableOpacity style={styles.checkboxContainer} activeOpacity={0.6} onPress={toggleTodoHandler}>
{todo.completed ? <Ionicon name="checkmark" size={20} color="gray" /> : null}
</TouchableOpacity>
<Animated.Text numberOfLines={2} style={[styles.text, textReanimatedStyle]}>
{todo.text}
</Animated.Text>
</View>
</Animated.View>
);
Related
In my app, I fetch users data from the server inside the useEffect hook and set the initialState of the useReducer. When action dispatch happens on text input change, the initialState resets instead of updating thus I can't type a word inside the input. Can someone figure out the problem, please? I'm fairly new to react reducers. I have attached the relevant codes. Thanks.
EditProfile.js
const EditProfile = ({ navigation, route }) => {
const userid = route.params.userid;
const { user } = React.useContext(AuthContext);
const [userData, setUserData] = React.useState(null);
const initialState = {
name: userData ? (userData.name ? userData.name : "") : "",
dob: userData ? (userData.dob ? userData.dob.toDate() : "") : "",
phone: userData ? (userData.phone ? userData.phone : "") : "",
location: userData ? (userData.location ? userData.location : "") : "",
caption: userData ? (userData.caption ? userData.caption : "") : "",
};
React.useEffect(() => {
async function fetchData() {
const response = await getUserData(userid);
setUserData(response);
}
fetchData();
}, []);
const reducer = (state, action) => {
switch (action.type) {
case "nameInputChange":
return {
...state,
name: action.name,
};
case "dobInputChange":
return {
...state,
dob: action.dob,
};
case "phoneInputChange":
return {
...state,
phone: action.phone,
};
case "locationInputChange":
return {
...state,
location: action.location,
};
case "captionInputChange":
return {
...state,
caption: action.caption,
};
}
};
const [data, dispatch] = React.useReducer(reducer, initialState);
const nameInputChange = (value) => {
dispatch({
type: "nameInputChange",
name: value,
});
console.log("Name: ", initialState.name);
};
const dobInputChange = (date) => {
dispatch({
type: "dobInputChange",
dob: date,
});
};
const phoneInputChange = (value) => {
dispatch({
type: "phoneInputChange",
phone: value,
});
};
const locationInputChange = (value) => {
dispatch({
type: "locationInputChange",
location: value,
});
};
const captionInputChange = (value) => {
dispatch({
type: "captionInputChange",
caption: value,
});
};
return (
<View>
<FlatList
showsVerticalScrollIndicator={false}
ListHeaderComponent={() => (
<View>
<TouchableOpacity>
<Image
source={require("../assets/images/profile.jpg")}
style={{ width: "98%", height: "98%", borderRadius: 59 }}
/>
<View>
<Entypo name="camera" color="#8000e3" size={18} />
</View>
</TouchableOpacity>
<View>
<VerticalNameInput
type="name"
label="Full Name"
placeholder="Full name"
color="#9798ac"
placeholder="enter your full name"
onChangeText={(value) => nameInputChange(value)}
value={initialState.name}
/>
<VerticalDateInput
label="Date of Birth"
color="#9798ac"
dobInputChange={dobInputChange}
initialState={initialState}
/>
</View>
<View>
<VerticalNameInput
type="mobile"
label="Mobile"
color="#9798ac"
placeholder="mobile number"
onChangeText={(value) => phoneInputChange(value)}
value={initialState.phone}
/>
<VerticalNameInput
type="location"
label="Location"
color="#9798ac"
placeholder="city and country"
onChangeText={(value) => locationInputChange(value)}
value={initialState.location}
/>
</View>
<View>
<VerticalNameInput
type="caption"
label="Profile Caption"
color="#9798ac"
placeholder="enter about yourself"
onChangeText={(value) => captionInputChange(value)}
value={initialState.caption}
/>
</View>
</View>
)}
/>
<View style={{ position: "relative", top: -90, left: 200 }}>
<FloatingAction
onPressMain={() => {
updateUser(userid, userData);
}}
floatingIcon={<Entypo name="check" size={28} color="#fff" />}
/>
</View>
</View>
);
};
export default EditProfile;
VerticalNameInput.js
const VerticalNameInput = ({ label, color, placeholder, type, ...rest }) => {
return (
<View>
<Text>
{label}
</Text>
<View>
<View>
{type === "name" ? (
<AntDesign name="user" color="#000" size={15} />
) : type === "mobile" ? (
<AntDesign name="mobile1" color="#000" size={15} />
) : type === "location" ? (
<EvilIcons name="location" color="#000" size={20} />
) : type === "caption" ? (
<Ionicons
name="information-circle-outline"
color="#000"
size={18}
/>
) : null}
</View>
<TextInput
style={{ width: "85%", height: "100%" }}
numberOfLines={1}
placeholder={placeholder}
placeholderTextColor={color}
{...rest}
/>
</View>
</View>
);
};
export default VerticalNameInput;
VerticalDateInput.js
const VerticalDateInput = ({ label, color, dobInputChange, initialState }) => {
const [date, setDate] = React.useState(new Date());
const [open, setOpen] = React.useState(false);
return (
<View>
<Text>
{label}
</Text>
<View>
<View>
<AntDesign name="calendar" color="#000" size={15} />
</View>
<View>
<Text style={{ marginLeft: 10 }}>
{initialState.dob
? initialState.dob.toDateString()
: new Date().toDateString()}
</Text>
<TouchableOpacity
style={{ marginRight: 10 }}
onPress={() => setOpen(true)}
>
<AntDesign name="caretdown" color="#000" size={12} />
</TouchableOpacity>
</View>
<DatePicker
maximumDate={new Date()}
mode="date"
modal
open={open}
date={initialState.dob ? initialState.dob : date}
onConfirm={(date) => {
setOpen(false);
setDate(date);
dobInputChange(date);
}}
onCancel={() => {
setOpen(false);
}}
/>
</View>
</View>
);
};
export default VerticalDateInput;
Try add "default" case return current state in your reducer.
It might happen that you dispatch some unknown action, and reducer return undefined as a result.
const reducer = (state = initialState, action) => {
switch (action.type) {
default:
// If this reducer doesn't recognize the action type, or doesn't
// care about this specific action, return the existing state unchanged
return state
}
}
I have something like this:
const BasketButton2 = ({ isWhite, style, navigation }) => (
<TouchableOpacity
style={[styles.button, style]}
onPress={() => Header.addInstaPost()}
>
<Icon
family="Entypo"
size={16}
name="new-message"
color={theme.COLORS[isWhite ? 'WHITE' : 'ICON']}
/>
</TouchableOpacity>
);
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {
notifications: false,
loading: true,
error: null,
modalVisible: false,
modalThanksVisible: false,
reportSubmitted: false,
reportError: false,
};
}
handleLeftPress = () => {
const { back, navigation } = this.props;
return back ? navigation.goBack() : navigation.openDrawer();
};
renderRight = () => {
const { white, title, navigation, scene } = this.props;
if (global.loggedUser === true) {
return [
<BasketButton2
key="basket-search"
navigation={navigation}
isWhite={white}
/>,
<ChatButton
key="chat-search"
navigation={navigation}
isWhite={white}
/>,
];
} else {
return [
<BasketButton
key="basket-search"
navigation={navigation}
isWhite={white}
/>,
<ChatButton
key="chat-search"
navigation={navigation}
isWhite={white}
/>,
];
}
};
renderSearch = () => {
const { navigation } = this.props;
return (
<Input
right
color="black"
style={styles.search}
placeholder="What are you looking for?"
onFocus={() => navigation.navigate('Search')}
iconContent={
<Icon
size={16}
color={theme.COLORS.MUTED}
name="magnifying-glass"
family="entypo"
/>
}
/>
);
};
renderOptions = () => {
const { navigation, optionLeft, optionRight } = this.props;
return (
<Block row style={styles.tabs}>
<Button
shadowless
style={[styles.tab, styles.divider]}
onPress={() => navigation.navigate('Categories')}
>
<Block row middle>
<Icon name="globe" family="feather" style={{ paddingRight: 8 }} />
<Text size={16} style={styles.tabTitle}>
{optionLeft || 'Locations'}
</Text>
</Block>
</Button>
<Button
shadowless
style={styles.tab}
onPress={() => navigation.navigate('Deals')}
>
<Block row middle>
<Icon name="grid" family="feather" style={{ paddingRight: 8 }} />
<Text size={16} style={styles.tabTitle}>
{optionRight || 'Categories'}
</Text>
</Block>
</Button>
</Block>
);
};
renderTabs = () => {
const { tabs, tabIndex, navigation } = this.props;
const defaultTab = tabs && tabs[0] && tabs[0].id;
if (!tabs) return null;
return (
<Tabs
data={tabs || []}
initialIndex={tabIndex || defaultTab}
onChange={(id) => navigation.setParams({ tabId: id })}
/>
);
};
renderHeader = () => {
const { search, tabs, options } = this.props;
if (search || tabs || options) {
return (
<Block center>
{search ? this.renderSearch() : null}
{options ? this.renderOptions() : null}
{tabs ? this.renderTabs() : null}
</Block>
);
}
return null;
};
addInstaPost = () => {
this.setState({ modalVisible: true });
};
render() {
const { back, title, white, transparent, navigation, scene } = this.props;
const noShadow = ['Profile'].includes(title);
const noShadowWhite = ['Search'].includes(title);
const headerStyles = [
!noShadow ? styles.shadow : null,
transparent ? { backgroundColor: 'rgba(0,0,0,0)' } : null,
];
var myHeaderStyle = styles.shadow;
if (noShadow) {
var myHeaderStyle = '';
} else if (transparent) {
var myHeaderStyle = "{ backgroundColor: 'rgba(0,0,0,0)' }";
} else if (noShadowWhite) {
var myHeaderStyle = styles.searchShadow;
}
return (
<Block style={myHeaderStyle}>
<View style={styles.imageContainer} transparent={transparent}>
{
this.renderInstaPostButton()
}
</View>
<View style={styles.item}>
<NavBar
back={back}
title={title}
style={styles.navbar}
transparent={transparent}
right={this.renderRight()}
rightStyle={{ alignItems: 'center' }}
leftStyle={{ paddingTop: 3, flex: 0.3 }}
leftIconName={back ? 'leftcircle' : 'menu-fold'}
leftIconFamily="AntDesign"
leftIconSize="1.6"
leftIconColor={
white ? materialTheme.COLORS.NAVICON : theme.COLORS.ICON
}
titleStyle={[
styles.title,
{ color: theme.COLORS[white ? 'WHITE' : 'ICON'] },
]}
onLeftPress={this.handleLeftPress}
/>
</View>
{this.renderHeader()}
</Block>
);
}
}
So basically inside BasketButton2 I am trying to make a call to a function which is inside the class Header.
onPress={() => Header.addInstaPost() is not working
as well as onPress={() => this.addInstaPost()
I am getting Header.addInstaPost is not defined.
How I can refer to function inside class?
Thanks!!
You can pass addInstaPost as a property to BasketButton2
class Header extends React.Component {
addInstaPost = () => {
this.setState({ modalVisible: true });
};
renderRight = () => {
const { white, title, navigation, scene } = this.props;
if (global.loggedUser === true) {
return [
<BasketButton2
key="basket-search"
navigation={navigation}
isWhite={white}
onPress={this.addInstaPost}
/>,
<ChatButton
key="chat-search"
navigation={navigation}
isWhite={white}
/>,
];
} else {
return [
<BasketButton
key="basket-search"
navigation={navigation}
isWhite={white}
/>,
<ChatButton
key="chat-search"
navigation={navigation}
isWhite={white}
/>,
];
}
};
}
const BasketButton2 = ({ isWhite, style, navigation, onPress }) => (
<TouchableOpacity
style={[styles.button, style]}
onPress={onPress}
>
<Icon
family="Entypo"
size={16}
name="new-message"
color={theme.COLORS[isWhite ? 'WHITE' : 'ICON']}
/>
</TouchableOpacity>
);
IMO this is the most prefered way for such case
I'm trying to migrate to react navigation 5, but I get an error where I use the DrawerItemList in place of the DrawerNavigatorItems.
My code used to look like this:
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>
)}
...
Now it looks like this:
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>
);
};
The error I get is:
TypeError: label is not a function. (In 'label({
color: color,
focused: focused
})', 'label' is an instance of Object)
and it's generated at the <DrawerItemList {...props} />
In the docs I read:
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>
);
}
The first screen that I render is a Navigator, the GameNavigator.
Could that be a problem?
I read in a issue that:
"It's not possible to add navigators inside drawer content. You can achieve custom layouts using a custom router and custom navigator:" source,but I've learned from a course that it is possible! Or does this guy mean something else with drawer content?
The GameNavigator is:
const GameStackNavigator = createStackNavigator();
const GameNavigator = () => {
return (
<GameStackNavigator.Navigator
initialRouteName="Welcome"
screenOptions={defaultNavOptions}
>
<GameStackNavigator.Screen
name="Welcome"
component={WelcomeScreen}
options={WelcomeScreenOptions}
/>
...
</GameStackNavigator.Navigator>
);
};
And the WelcomeScreen is:
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>
)
};
};
Any suggestion would be appreciated.
Thanks
For drawerLabel you have directly set an object which is wrong
drawerLabel: (
<BoldText style={NavigationStyles.arhiki}>Αρχική</BoldText>
),
This should be either a string or a function that returns a component, so you should change it like below
drawerLabel: {()=>(
<BoldText style={NavigationStyles.arhiki}>Αρχική</BoldText>
)},
You can refer the docs
https://reactnavigation.org/docs/drawer-navigator/#drawerlabel
I'm migrating a RN project version 4 to 5.
I was using the tabBarComponent option to replace tab bar component with a custom one. Old docs.
How to achieve the same in version 5, I can't find this option in the new docs.
Here is the new API way to achieve a custom tab component:
import { View, Text, TouchableOpacity } from 'react-native';
function MyTabBar({ state, descriptors, navigation }) {
return (
<View style={{ flexDirection: 'row' }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{ flex: 1 }}
>
<Text style={{ color: isFocused ? '#673ab7' : '#222' }}>
{label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
// ...
<Tab.Navigator tabBar={props => <MyTabBar {...props} />}>
{...}
</Tab.Navigator>
This link will surely help.
https://reactnavigation.org/docs/en/next/bottom-tab-navigator.html#tabbar
Hope this helps. Cheers!
I have a modal that contain icons and description and status, and i want to pass the icons and descriptions from index to the modal,I already pass the status. is there anyway to do that? sorry i'm still new to react native and thanks in advance
this is my index.js
export const img =
{
itemStatus: {
"Open": { name: 'open-book', type: 'entypo', color: '#ffb732', desc:'New Attribut, New Attention'},
"Approved": { name: 'checklist', type: 'octicon', color: '#3CB371', desc:'Approved by SPV/MNG' },
"Escalated": { name: 'mail-forward', type: 'font-awesome', color: '#ffb732', desc:'Escalated to SPV/MNG' },
"Deliver Partial": { name: 'arrange-send-to-back', type: 'material-community', color: '#8B4513', desc:'Some items in a DO have not arrived/was faulty' },
};
and this is my container
class MyRequest extends React.Component {
constructor() {
super();
this.state = {
currentStatus: null,
refreshing: false,
fetchStatus: null
};
handleShowModal = (status) =>{
this.setState({
currentStatus: status,
});
}
handleDismissModal = () =>{
this.setState({currentStatus: null});
}
<View style={[styles.panelContainer, status === 'success' ? {} : { backgroundColor: color.white }]}>
<FlatList
showsVerticalScrollIndicator={false}
progressViewOffset={-10}
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
onMomentumScrollEnd={(event) => event.nativeEvent.contentOffset.y === 0 ? this.onRefresh() : null}
data={content}
renderItem={({ item }) => item}
keyExtractor={(item, key) => key.toString()}
/>
</View>
<IconModal visible={this.state.modalVisible} close={this.handleDismissModal} icon={} status={this.state.currentStatus} desc={} />
}
and this is my modal
const IconModal = (props) => {
return(
<Modal
isVisible={props.visible}
onBackdropPress={props.close}
>
<View style={styles.dialogBox}>
<View style={styles.icon}>
<Icon>{props.icon}</Icon>
</View>
<View style={styles.text}>
<Text style={styles.status}>{props.status}</Text>
<Text>{props.desc}</Text>
</View>
<TouchableOpacity onPress={props.close}>
<View>
<Text style={styles.buttonText}>GOT IT</Text>
</View>
</TouchableOpacity>
</View>
</Modal>
)
}
It's a bit unclear how you plan on mapping against img.itemStatus index but you can just reference the object you want as such.
import img from '....path_to_index.js'
...
// const currentItemStatus = img.itemStatus.Open
// OR
const itemStatus = 'Open' // Or 'Approved', 'Escalated', 'Deliver Partial'
const currentItemStatus = img.itemStatus[itemStatus]
...
<IconModal
visible={this.state.modalVisible}
close={this.handleDismissModal}
icon={currentItemStatus.name} // Passing name
status={this.state.currentStatus}
desc={currentItemStatus.desc} // Passing desc
/>
...
Hope this was helpful