react-native-element-dropdown returns undefined, onchange not working in the second dropdown - react-native

I have two dropdowns from react native element dropdown, the first one works fine and when i choose the restaurant option i display a second dropdwon, but this one the onChange always returns undefined.
Here is my code
export function EcoForm(props) {
const { formik } = props;
const [isEnabled, setIsEnabled] = useState(false);
const toggleSwitch = () => {
setIsEnabled((previousState) => !previousState);
formik.setFieldValue("eco", isEnabled);
};
const dataBusinessType = [
{ label: "Restaurant", value: "restaurant" },
{ label: "Shop", value: "shop" },
{ label: "Acomodation", value: "acomodation" },
];
const dataRestaurantType = [
{ label: "RestaurantTYpe", value: "restaurantType" },
{ label: "Cofee/Bakery", value: "cofee/Bakery" },
];
const [value, setValue] = useState(null);
const [valueRestaurantType, setValueRestaurantType] = useState(null);
// console.log(isEnabled);
return (
<View style={styles.content}>
<Text style={styles.text}>Is the business Ecofriendly?</Text>
<Dropdown
style={styles.dropdown}
placeholderStyle={styles.placeholderStyle}
selectedTextStyle={styles.selectedTextStyle}
inputSearchStyle={styles.inputSearchStyle}
iconStyle={styles.iconStyle}
data={dataBusinessType}
search
maxHeight={300}
labelField="label"
valueField="value"
placeholder="Select business type"
searchPlaceholder="Search..."
value={value}
onChange={(item) => {
setValue(item.value);
formik.setFieldValue("businessType", value);
}}
renderLeftIcon={() => (
<AntDesign
style={styles.icon}
color="black"
name="Safety"
size={20}
/>
)}
/>
{value === "restaurant" ? (
<>
<Dropdown
style={styles.dropdown}
placeholderStyle={styles.placeholderStyle}
selectedTextStyle={styles.selectedTextStyle}
inputSearchStyle={styles.inputSearchStyle}
iconStyle={styles.iconStyle}
data={dataRestaurantType}
search
maxHeight={300}
labelField="label"
valueField="value"
placeholder="Select restaurant type"
searchPlaceholder="Search..."
value={valueRestaurantType}
onChange={(item) => {
setValueRestaurantType(item.valueRestaurantType);
formik.setFieldValue("restauranType", valueRestaurantType);
console.log("a ver ", valueRestaurantType);
}}
renderLeftIcon={() => (
<AntDesign
style={styles.icon}
color="black"
name="Safety"
size={20}
/>
)}
/>
<CheckBox
title="Eco friendly"
checked={isEnabled}
onPress={() => toggleSwitch()}
/>
<CheckBox
title="Vegan"
checked={isEnabled}
onPress={() => toggleSwitch()}
/>
</>
) : null}
</View>
);
}
I have also tried creating a component with the second dropdown and import it but the behaviour is exactly the same.
Not sure what i am missing.

As I can see, the Dropdown Component in react doesn't have any parameter named as Data.
Try to change Data with options like this.
<Dropdown
style={styles.dropdown}
placeholderStyle={styles.placeholderStyle}
selectedTextStyle={styles.selectedTextStyle}
inputSearchStyle={styles.inputSearchStyle}
iconStyle={styles.iconStyle}
options={dataRestaurantType}
search
maxHeight={300}
labelField="label"
valueField="value"
placeholder="Select restaurant type"
searchPlaceholder="Search..."
value={valueRestaurantType}
onChange={(item) => {
setValueRestaurantType(item.valueRestaurantType);
formik.setFieldValue("restauranType", valueRestaurantType);
console.log("a ver ", valueRestaurantType);
}}
renderLeftIcon={() => (
<AntDesign
style={styles.icon}
color="black"
name="Safety"
size={20}
/>
)}
/>
Try it out, it worked in my case.

Check the below code it's working fine please check the below logs SS:
import React, { memo, useEffect, useRef, useState } from 'react';
import { Text, View, StyleSheet,CheckBox } from 'react-native';
import Constants from 'expo-constants';
import { Dropdown } from 'react-native-element-dropdown';
import AntDesign from 'react-native-vector-icons/AntDesign';
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default function App(props) {
const { formik } = props;
const [isEnabled, setIsEnabled] = useState(false);
const toggleSwitch = () => {
setIsEnabled((previousState) => !previousState);
formik.setFieldValue("eco", isEnabled);
};
const dataBusinessType = [
{ label: "Restaurant", value: "restaurant" },
{ label: "Shop", value: "shop" },
{ label: "Acomodation", value: "acomodation" },
];
const dataRestaurantType = [
{ label: "RestaurantTYpe", value: "restaurantType" },
{ label: "Cofee/Bakery", value: "cofee/Bakery" },
];
const [value, setValue] = useState(null);
const [valueRestaurantType, setValueRestaurantType] = useState(null);
// console.log(isEnabled);
return (
<View style={styles.content}>
<Text style={styles.text}>Is the business Ecofriendly?</Text>
<Dropdown
style={styles.dropdown}
placeholderStyle={styles.placeholderStyle}
selectedTextStyle={styles.selectedTextStyle}
inputSearchStyle={styles.inputSearchStyle}
iconStyle={styles.iconStyle}
data={dataBusinessType}
search
maxHeight={300}
labelField="label"
valueField="value"
placeholder="Select business type"
searchPlaceholder="Search..."
value={value}
onChange={(item) => {
console.log("item==>",item)
setValue(item.value);
formik.setFieldValue("businessType", value);
}}
renderLeftIcon={() => (
<AntDesign
style={styles.icon}
color="black"
name="Safety"
size={20}
/>
)}
/>
{value === "restaurant" ? (
<>
<Dropdown
style={styles.dropdown}
placeholderStyle={styles.placeholderStyle}
selectedTextStyle={styles.selectedTextStyle}
inputSearchStyle={styles.inputSearchStyle}
iconStyle={styles.iconStyle}
data={dataRestaurantType}
search
maxHeight={300}
labelField="label"
valueField="value"
placeholder="Select restaurant type"
searchPlaceholder="Search..."
value={valueRestaurantType}
onChange={(item) => {
console.log("a ver ", item);
setValueRestaurantType(item.valueRestaurantType);
formik.setFieldValue("restauranType", item.valueRestaurantType);
}}
renderLeftIcon={() => (
<AntDesign
style={styles.icon}
color="black"
name="Safety"
size={20}
/>
)}
/>
<CheckBox
title="Eco friendly"
checked={isEnabled}
onPress={() => toggleSwitch()}
/>
<CheckBox
title="Vegan"
checked={isEnabled}
onPress={() => toggleSwitch()}
/>
</>
) : null}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});

hope this will help you
const App=()=>{
retutn(
<Dropdown
style={[style.dropdown1, isFocus && { borderColor: 'blue' }]}
selectedTextStyle={style.selectedTextStyle1}
data={data}
maxHeight={300}
labelField="label"
valueField="value"
placeholder={!isFocus ? 'Personal' : ''}
onFocus={() => setIsFocus(true)}
onBlur={() => setIsFocus(false)}
onChange={item => {
setValue(item.label);
setIsFocus(false);
}}
/>
)
}
const styel=styleSheet.create({
dropdown1: {
height: 50,
width: 350,
marginLeft: 15,
borderColor: 'gray',
//borderWidth: 0.5,
borderRadius: 8,
paddingHorizontal: 8,
},
selectedTextStyle1: {
fontSize: 16,
},
})

Related

initialState resets after dispatch actions in react useReducer

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
}
}

React Native Scroll View not working with

Having an issue with my ScrollView. I use it in a couple different places in my application, and most of them are working exactly as expected.
However, in one component it is working very strangely - if I swipe quickly, it will sometimes work, but usually not, and if I swipe gently or only a small amount, it doesn't work at all. I render a couple different things inside the ScrollView, but can't work out why any of them might be causing a problem, and can't spot anything obvious that's different between the one that doesn't work and the others, so I'm really at my wits end!
I am testing it on Android.
Here's what I think are the relevant bits of code for the page, but I've also put the full code below - please let me know if there's any other detail that would be useful:
const wait = (timeout) => {
return new Promise((resolve) => setTimeout(resolve, timeout));
};
export default function PotluckStandalone(props) {
const potlucks = useSelector((state) => state.potlucks);
const potluck = potlucks.find(
({ idCode }) => idCode === props.route.params.idCode
);
const [refreshing, setRefreshing] = React.useState(false);
const onRefresh = React.useCallback(() => {
setRefreshing(true);
wait(2000).then(() => setRefreshing(false));
}, []);
const dispatch = useDispatch();
const Reply = () => {
return (
<View>
<FlatList
keyExtractor={(item, index) => index}
data={potluck.replies}
renderItem={({ item }) => (
<View>
<Card
containerStyle={{
borderRadius: 12,
borderWidth: 1,
elevation: 0,
backgroundColor: "rgba(255,255,255,0.6)",
overflow: "hidden",
}}
style={{ borderColor: "rgba(255,255,255,0.1)" }}
>
<Card.Title>{item.bringer} is bringing...</Card.Title>
<Card.Divider />
{item.bringing.map((bringItem, index) => {
return (
<Text key={index}>
{bringItem}
{index < item.bringing.length - 2 ? ", " : ""}
{index === item.bringing.length - 2 ? " and " : ""}
</Text>
);
})}
</Card>
</View>
)}
/>
</View>
);
};
if (!potluck) {
return <Text>Loading...</Text>;
} else {
return (
<ImageBackground
source={require("../images/background.png")}
style={{ width: "100%", height: "100%", alignItems: "center" }}
>
<ScrollView
style={styles.page}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
>
<Card
containerStyle={{
borderRadius: 12,
borderWidth: 1,
elevation: 0,
backgroundColor: "rgba(255,255,255,0.6)",
overflow: "hidden",
}}
style={{ borderColor: "rgba(255,255,255,0.1)" }}
>
<Card.Title>
<Text>{potluck.potluckTitle}</Text>
</Card.Title>
<Card.Divider />
<Text>Host: {potluck.potluckHost}</Text>
<Text>Theme: {potluck.potluckTheme}</Text>
<Text>
Essentials:
{potluck.essentials.map((essential, index) => {
return (
<Text key={index}>
{" "}
{essential}
{index < potluck.essentials.length - 2 ? ", " : ""}
{index === potluck.essentials.length - 2 ? " and " : ""}
</Text>
);
})}
</Text>
<Card.Divider />
<Reply />
</Card>
<Bringing
potluck={potluck}
setReplySnack={() => setReplySnack(true)}
/>
</ScrollView>
</ImageBackground>
);
}
}
const styles = StyleSheet.create({
page: {
width: "90%",
paddingTop: 50,
paddingBottom: 250,
},
});
Full code here:
import { StatusBar } from "expo-status-bar";
import React, { useState, useEffect } from "react";
import { useSelector } from "react-redux";
import {
ScrollView,
View,
Text,
FlatList,
RefreshControl,
SafeAreaView,
Button,
Share,
ImageBackground,
} from "react-native";
import { useDispatch } from "react-redux";
import { Card } from "react-native-elements";
import Bringing from "./Bringing";
import { updatePotluck } from "../actions/potlucks";
import { render } from "react-dom";
import { StyleSheet } from "react-native";
import Snackbar from "react-native-snackbar-component";
const wait = (timeout) => {
return new Promise((resolve) => setTimeout(resolve, timeout));
};
export default function PotluckStandalone(props) {
const potlucks = useSelector((state) => state.potlucks);
const potluck = potlucks.find(
({ idCode }) => idCode === props.route.params.idCode
);
const [refreshing, setRefreshing] = React.useState(false);
const onRefresh = React.useCallback(() => {
setRefreshing(true);
wait(2000).then(() => setRefreshing(false));
}, []);
const dispatch = useDispatch();
const [potluckSnackIsVisible, setPotluckSnackIsVisible] = useState(false);
const [replySnackVisible, setReplySnackVisible] = useState(false);
React.useEffect(() => {
props.route.params.success
? setPotluckSnackIsVisible(true)
: setPotluckSnackIsVisible(false);
}, []);
const onShare = async () => {
try {
const result = await Share.share({
message: `Join me for a potluck | whatLuck https://whatluck.netlify.app/potlucks/${potluck.idCode}`,
});
if (result.action === Share.sharedAction) {
if (result.activityType) {
// shared with activity type of result.activityType
} else {
// shared
}
} else if (result.action === Share.dismissedAction) {
// dismissed
}
} catch (error) {
alert(error.message);
}
};
const setReplySnack = () => setReplySnackVisible(true);
const Reply = () => {
return (
<View>
<FlatList
keyExtractor={(item, index) => index}
data={potluck.replies}
//style={styles.flatlist}
renderItem={({ item }) => (
<View>
<Card
containerStyle={{
borderRadius: 12,
borderWidth: 1,
elevation: 0,
backgroundColor: "rgba(255,255,255,0.6)",
overflow: "hidden",
}}
style={{ borderColor: "rgba(255,255,255,0.1)" }}
>
<Card.Title>{item.bringer} is bringing...</Card.Title>
<Card.Divider />
{item.bringing.map((bringItem, index) => {
return (
<Text key={index}>
{bringItem}
{index < item.bringing.length - 2 ? ", " : ""}
{index === item.bringing.length - 2 ? " and " : ""}
</Text>
);
})}
</Card>
</View>
)}
/>
</View>
);
};
if (!potluck) {
return <Text>Loading...</Text>;
} else {
return (
<ImageBackground
source={require("../images/background.png")}
style={{ width: "100%", height: "100%", alignItems: "center" }}
>
<ScrollView
style={styles.page}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
>
<Card
containerStyle={{
borderRadius: 12,
borderWidth: 1,
elevation: 0,
backgroundColor: "rgba(255,255,255,0.6)",
overflow: "hidden",
}}
style={{ borderColor: "rgba(255,255,255,0.1)" }}
>
<Card.Title>
<Text>{potluck.potluckTitle}</Text>
</Card.Title>
<Card.Divider />
<Button onPress={onShare} title="Invite your friends" />
<Text>Host: {potluck.potluckHost}</Text>
<Text>Theme: {potluck.potluckTheme}</Text>
<Text>
Essentials:
{potluck.essentials.map((essential, index) => {
return (
<Text key={index}>
{" "}
{essential}
{index < potluck.essentials.length - 2 ? ", " : ""}
{index === potluck.essentials.length - 2 ? " and " : ""}
</Text>
);
})}
</Text>
<Card.Divider />
<Reply />
</Card>
<Bringing
potluck={potluck}
setReplySnack={() => setReplySnack(true)}
/>
<Snackbar
visible={potluckSnackIsVisible}
textMessage="Potluck created successfully!"
autoHidingTime={3000}
/>
<Snackbar
visible={replySnackVisible}
textMessage="Reply posted successfully!"
autoHidingTime={3000}
/>
</ScrollView>
</ImageBackground>
);
}
}
const styles = StyleSheet.create({
page: {
width: "90%",
paddingTop: 50,
paddingBottom: 250,
},
});

How to Open left side drawer and right side drawer in react native project

I wrote the below code to make multiple side drawer but unluckily I am not able.
Here open the left side drawer easily but the right-side drawer is not open. for Analyze purpose header.js file when I used here onPress={() => navigation.openDrawer()} then open left side drawer and I also used in second drawer element onPress={() => navigation.openDrawer()} also open left ride drawer but I want to open right side drawer.
What should I do?
app.js file:
import * as React from 'react';
import {View, Button,StyleSheet, Alert, Text, ActivityIndicator, Image} from 'react-native';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import {createDrawerNavigator} from '#react-navigation/drawer';
import DrawerContent from './screens/common/DrawerContent';
import SearchDrawer from './screens/common/SearchDrawer';
import Registration from './screens/authentication/Registration';
import Login from './screens/authentication/Login';
import ForgotPassword from './screens/authentication/ForgotPassword';
import ChangePassword from './screens/authentication/ChangePassword';
import Dashboard from './screens/user/Dashboard';
import Notifications from './screens/user/Notifications';
import ProfileStep from './screens/user';
import Shipment from './screens/user/shipment';
// import SplashScreen from 'react-native-splash-screen';
// import {LogBox} from 'react-native';
import {Provider} from 'react-redux';
import AsyncStorage from '#react-native-community/async-storage';
import {store, getAsyncStorage} from './redux/store';
import messaging from '#react-native-firebase/messaging';
const Stack = createStackNavigator();
const DrawerR = createDrawerNavigator();
const DrawerL = createDrawerNavigator();
function User(props) {
const [userInfo, setUserInfo] = React.useState('');
React.useEffect(() => {
getUserInfo();
}, []);
const getUserInfo = async () => {
let userInfo = await AsyncStorage.getItem('userInfo');
if (userInfo) {
store.dispatch(getAsyncStorage());
userInfo = JSON.parse(userInfo);
setUserInfo(userInfo);
}
};
return (
<DrawerL.Navigator
drawerContent={(props) => (
<DrawerContent {...props} size="22" data={userInfo} />
)}
// drawerPosition="right"
// drawerContent={(props) => (
// <SearchDrawer {...props} size="22" />
// )}
>
{props.route.params !== undefined && props.route.params.completed ? (
<>
<DrawerL.Screen name="Dashboard" component={Dashboard} />
<DrawerL.Screen name="ProfileStep" component={ProfileStep} />
</>
) : (
<>
<DrawerL.Screen name="ProfileStep" component={ProfileStep} />
<DrawerL.Screen name="Dashboard" component={Dashboard} />
</>
)}
<DrawerL.Screen name="Shipment" component={Shipment} />
</DrawerL.Navigator>
);
}
function User1(props){
const [userInfo1, setUserInfo1] = React.useState('')
React.useEffect(() => {
getUserInfo1();
}, []);
const getUserInfo1 = async () => {
let userInfo1 = await AsyncStorage.getItem('userInfo');
if (userInfo1) {
store.dispatch(getAsyncStorage());
userInfo1 = JSON.parse(userInfo1);
setUserInfo1(userInfo1);
}
};
return (
<DrawerR.Navigator
drawerPosition="right"
drawerContent={(props) => (
<SearchDrawer {...props} size="22" data={userInfo1}/>
)}
>
</DrawerR.Navigator>
);
}
function App() {
React.useEffect(() => {
const unsubscribe = messaging().onMessage(async (remoteMessage) => {
Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
});
return unsubscribe;
}, []);
return (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false,
}}>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Registration" component={Registration} />
<Stack.Screen name="ForgotPassword" component={ForgotPassword} />
<Stack.Screen name="ChangePassword" component={ChangePassword} />
<Stack.Screen name="User" component={User} />
<Stack.Screen name="User1" component={User1} />
<Stack.Screen name="Notifications" component={Notifications} />
</Stack.Navigator>
</NavigationContainer>
</Provider>
);
}
export default App;
DrawerContent.js
import React,{useEffect} from 'react';
import {View, StyleSheet, Alert, Text, ActivityIndicator, Image} from 'react-native';
import {
Avatar,
Title,
Caption,
Drawer,
TouchableRipple,
Divider,
Switch,
} from 'react-native-paper';
import {DrawerContentScrollView, DrawerItem} from '#react-navigation/drawer';
import AsyncStorage from '#react-native-community/async-storage';
import {connect} from 'react-redux';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import {setTheme} from './../../redux/actions/config';
import {getThemeColors} from '../../global/themes';
import {API_URL} from './../../global/config';
import { Linking } from 'react-native';
// import {bindActionCreators} from 'redux';
var pkg = require('./../../package.json');
function DrawerContent(props) {
const [loading, setLoading] = React.useState(false);
const [profileStatus, setProfileStatus] = React.useState({});
const [whitelabel, setIfWhiteLabel] = React.useState('')
// const [isDarkMode, setIsDarkMode] = React.useState(false);
let userInfo = props.data;
const logout = () => {
Alert.alert(
'Are you sure?',
'You want to Logout',
[
{
text: 'Cancel',
style: 'cancel',
},
{
text: 'Yes',
onPress: () => {
setLoading(true);
console.log(`${API_URL}disableDevice`);
fetch(`${API_URL}disableDevice`, {
method: 'post',
body: JSON.stringify({user_id: userInfo.id}),
})
.then((res) => res.json())
.then(async (res) => {
if (!res.status) {
setLoading(false);
alert('something went wrong!!');
} else {
try {
await AsyncStorage.removeItem('userInfo');
await AsyncStorage.removeItem('profileStatus');
props.navigation.reset({
index: 0,
routes: [{name: 'Login'}],
});
} catch (exception) {
setLoading(false);
return false;
}
}
})
.catch((e) => {
setLoading(false);
console.log(e);
});
},
},
],
{cancelable: false},
);
};
const onThemeChange = async () => {
AsyncStorage.setItem(
'themeMode',
props.theme === 'default' ? 'dark' : 'default',
);
props.onSelectTheme(props.theme === 'default' ? 'dark' : 'default');
fetch(`${API_URL}toggleDarkMode`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
user_id: userInfo.id,
dark_mode: props.theme === 'default' ? 1 : 0,
}),
})
.then((res) => res.json())
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
};
const {colors} = props;
React.useEffect(() => {
var profileStatus = props.profileStatus;
if (profileStatus) {
profileStatus = JSON.parse(profileStatus);
setProfileStatus(profileStatus);
}
checkIfWhiteLabel()
}, [props.profileStatus]);
useEffect(() => {
checkIfWhiteLabel()
}, [])
const checkIfWhiteLabel = async () =>{
let iftru = await AsyncStorage.getItem('whiteLabel');
if(iftru == null){
setIfWhiteLabel('false')
}else{
setIfWhiteLabel(iftru)
}
}
return (
<>
<View style={{flex: 1, backgroundColor: colors.drawerBackground}}>
<DrawerContentScrollView {...props}>
<View style={styles.drawerContent}>
<View style={styles.tinyLogoDiv}>
<Image
style={styles.tinyLogo}
source={require('../../assets/logo.png')}
/>
</View>
<TouchableRipple
style={styles.userInfoSection}
onPress={() =>
props.navigation.navigate('ProfileStep', {status: 'TRUE'})
}
rippleColor="rgba(0, 0, 0, .32)">
<View style={{flexDirection: 'row'}}>
<Avatar.Image
source={require('../../assets/avater.png')}
size={50}
/>
<View style={{marginLeft: 15, flexDirection: 'column'}}>
<Title style={styles.title(colors.textColor)}>
{profileStatus.username ?? userInfo.name}
</Title>
<Caption style={styles.caption(colors.textColor)}>
{profileStatus.email ?? userInfo.email}
</Caption>
</View>
<View
style={{
justifyContent: 'center',
marginLeft: 'auto',
paddingRight: 7,
}}>
<Icon
name="account-edit"
size={25}
color={colors.textColor}
/>
</View>
</View>
</TouchableRipple>
<Divider />
<Drawer.Section style={styles.drawerSection}>
<DrawerItem
icon={() => (
<Icon name="home" size={30} color={colors.textColor} />
)}
labelStyle={{color: colors.textColor}}
label="Home"
onPress={() => {
if (profileStatus.type || profileStatus.type === undefined) {
alert('Complete Your profile first');
} else {
props.navigation.navigate('Dashboard');
}
}}
/>
<DrawerItem
icon={() => (
<Icon
name="text-box-plus"
size={30}
color={colors.textColor}
/>
)}
label="Parcel"
labelStyle={{color: colors.textColor}}
onPress={() => {
if (profileStatus.type || profileStatus.type === undefined) {
alert('Complete Your profile first');
} else {
props.navigation.navigate('Shipment');
}
}}
/>
</Drawer.Section>
</View>
</DrawerContentScrollView>
<Drawer.Section style={styles.bottomDrawerSection}>
<DrawerItem
style={{paddingBottom: 0}}
icon={() =>
loading ? (
<ActivityIndicator size={30} color={colors.primaryColor} />
) : (
<Icon name="exit-to-app" size={30} color={colors.textColor} />
)
}
label={'Logout'}
labelStyle={{color: colors.textColor}}
onPress={() => logout()}
/>
</Drawer.Section>
<View
style={{
marginBottom: 5,
alignItems: 'center',
}}>
<Text style={{opacity: 0.8, color: colors.textColor}}>
V - {pkg.version}
</Text>
</View>
</View>
</>
);
}
const mapStateToProps = (state) => {
let imagePlaceholder = JSON.parse(state.brand);
if (imagePlaceholder) {
imagePlaceholder = imagePlaceholder.default.avatar;
} else {
imagePlaceholder = '';
}
var theme = getThemeColors(state.theme);
return {
theme: state.theme,
colors: theme,
profileStatus: state.profileStatus,
imagePlaceholder: imagePlaceholder,
};
// count: state.count,
};
export default connect(mapStateToProps, {
onSelectTheme: setTheme,
})(DrawerContent);
//
const styles = StyleSheet.create({
drawerContent: {
flex: 1,
},
userInfoSection: {
paddingLeft: 20,
paddingVertical: 10,
},
title: (color) => ({
fontSize: 16,
marginTop: 3,
fontWeight: 'bold',
color: color,
}),
caption: (color) => ({
fontSize: 12,
lineHeight: 14,
opacity: 0.8,
color: color,
marginLeft:-15,
}),
row: {
marginTop: 20,
flexDirection: 'row',
alignItems: 'center',
},
section: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 15,
},
paragraph: {
fontWeight: 'bold',
marginRight: 3,
},
drawerSection: {
marginTop: 15,
},
bottomDrawerSection: {
// marginBottom: 15,
borderTopColor: '#ccc',
borderTopWidth: 0.2,
borderBottomColor: '#ccc',
borderBottomWidth: 0.2,
},
preference: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: 12,
paddingHorizontal: 16,
},
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
opacity: 0.6,
justifyContent: 'center',
alignItems: 'center',
},
loadingText: {
fontSize: 20,
paddingTop: 20,
fontWeight: 'bold',
},
tinyLogoDiv:{
// width:100,
},
tinyLogo: {
width: 150,
height: 50,
// alignItems:'center',
marginLeft:70
},
});
SearchDrawer.js
import React from 'react';
import { View, StyleSheet, Text, Button,TextInput } from 'react-native';
import { DrawerContentScrollView} from '#react-navigation/drawer';
import { setTheme } from './../../redux/actions/config';
import { getThemeColors } from '../../global/themes';
import { connect } from 'react-redux';
function SearchDrawer(props) {
return (
<>
<View style={{ flex: 1, backgroundColor: '#ffffff' }}>
<DrawerContentScrollView {...props}>
<View>
<Text>This is input Field</Text>
<TextInput
label='Search here'
style={styles.input}
placeholder="Search By Order Id"
/>
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
</View>
</DrawerContentScrollView>
</View>
</>
);
}
const mapStateToProps = (state) => {
let imagePlaceholder = JSON.parse(state.brand);
if (imagePlaceholder) {
imagePlaceholder = imagePlaceholder.default.avatar;
} else {
imagePlaceholder = '';
}
var theme = getThemeColors(state.theme);
return {
theme: state.theme,
colors: theme,
profileStatus: state.profileStatus,
imagePlaceholder: imagePlaceholder,
};
};
const styles = StyleSheet.create({
drawerSection: {
marginTop: 15,
},
})
export default connect(mapStateToProps, {
onSelectTheme: setTheme,
})(SearchDrawer);
Header.js
import React from 'react';
import {
// StyleSheet,
// TouchableOpacity,
// TouchableWithoutFeedback,
Text,
View, TouchableOpacity,
AppState, TextInput, StyleSheet
} from 'react-native';
import { DrawerItem } from '#react-navigation/drawer';
import { DrawerActions } from '#react-navigation/native';
import { useNavigation } from '#react-navigation/native';
import { Appbar, Badge } from 'react-native-paper';
// import {colors} from '../../global/themes';
import { FontAwesomeIcon } from '#fortawesome/react-native-fontawesome'
import { faCoffee } from '#fortawesome/free-solid-svg-icons'
import Icon from 'react-native-vector-icons/FontAwesome';
import { connect } from 'react-redux';
import { setTheme } from './../../redux/actions/config';
// import {bindActionCreators} from 'redux';
import { getThemeColors } from '../../global/themes';
// import {useState} from 'react';
import { API_URL } from '../../global/config';
import AsyncStorage from '#react-native-community/async-storage';
import Menu, { MenuItem } from 'react-native-material-menu';
import messaging from '#react-native-firebase/messaging';
import notifee, { EventType } from '#notifee/react-native';
const ContentTitle = ({ title, color }) => (
<Appbar.Content
title={
// <View style={{zIndex: 99999999}}>
<Text style={{ fontSize: 20, color: color, zIndex: 99999999 }}>
{' '}
{title}{' '}
</Text>
// </View>
}
style={{ marginLeft: -10 }}
/>
);
function Header(props) {
const navigation = useNavigation();
console.log('Pera Navigation', navigation)
const { colors } = props;
const [notificationCount, setNotificationCount] = React.useState(0);
const [showMenu, setShowMenu] = React.useState(false);
const [appState, setAppState] = React.useState(AppState.currentState);
const [actions, setActions] = React.useState([]);
const menu = React.useRef();
const getUserId = async () => {
let userInfo = await AsyncStorage.getItem('userInfo');
if (userInfo) {
userInfo = JSON.parse(userInfo);
return userInfo.id;
}
};
function getNotificationCount() {
getUserId().then((id) => {
console.log(`${API_URL}newNotification`);
fetch(`${API_URL}newNotification`, {
method: 'post',
body: JSON.stringify({ user_id: id }),
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
.then((res) => res.json())
.then((res) => {
if (res.status) {
setNotificationCount(res.notification_count);
}
})
.catch((e) => {
console.log(e);
});
});
}
const _handleAppStateChange = (nextAppState) => {
getNotificationCount();
setAppState(nextAppState);
};
React.useEffect(() => {
AppState.addEventListener('change', (e) => _handleAppStateChange(e));
getNotificationCount();
notifee.createChannel({
id: 'custom-sound',
name: 'System Sound',
sound: 'notification__.mp3',
});
notifee.onForegroundEvent(({ type, detail }) => {
switch (type) {
case EventType.DISMISSED:
console.log('User dismissed notification', detail.notification);
break;
case EventType.PRESS:
let count = notificationCount;
setNotificationCount(0);
// alert('0 done');
navigation.navigate('Notifications', {
count: count,
screen: ' ',
});
break;
}
});
messaging().onMessage(async (remoteMessage) => {
// console.log('ok oko k');
await notifee.displayNotification({
title: remoteMessage.notification.title,
body: remoteMessage.notification.body,
android: {
channelId: 'custom-sound',
},
});
getNotificationCount();
});
messaging().onNotificationOpenedApp((remoteMessage) => {
let count = notificationCount;
setNotificationCount(0);
navigation.navigate('Notifications', {
count: count,
screen: 'header',
});
});
}, []);
React.useEffect(() => {
if (showMenu) {
menu.current.show();
}
}, [showMenu]);
return (
<Appbar.Header style={{ backgroundColor: colors.headerColor }}>
{props.back ? (
<Appbar.BackAction
onPress={() => (props.goBack ? props.goBack() : navigation.goBack())}
/>
) : (
<Appbar.Action
icon="menu"
onPress={() => navigation.openDrawer()}
size={28}
color={colors.textColor}
style={{ paddingLeft: 3 }}
/>
)}
<TouchableOpacity style={styles.searchSection}>
<TextInput
style={styles.input}
placeholder="Search By Order Id"
onPress={() =>navigation.openDrawer()}
/>
<Icon style={styles.searchIcon} name="search" size={20} color="#000" />
</TouchableOpacity>
{/* <TouchableOpacity onPress={() => navigation.openDrawer()}>
<Icon style={{marginLeft: 10}} name='menu'/>
</TouchableOpacity> */}
{/* <Button title='open the drawer' onPress={this.props.navigation.dispatch(DrawerActions.openDrawer('drawerOpenLeft'))}></Button>
<Button title='open the drawer right' onPress={this.props.navigation.dispatch(DrawerActions.openDrawer('drawerOpenRight'))}></Button> */}
<ContentTitle title={props.title} color={colors.textColor} />
{!props.hideNotification && (
<View>
{notificationCount > 0 && (
<Badge
visible={true}
size={16}
style={{ position: 'absolute', top: 10, right: 7, zIndex: 9999 }}>
<Text style={{ fontSize: 12 }}>{notificationCount}</Text>
</Badge>
)}
<Appbar.Action
icon="bell"
onPress={() => {
let count = notificationCount;
setNotificationCount(0);
navigation.navigate('Notifications', {
count: count,
screen: 'header',
});
}}
size={28}
color={colors.textColor}
style={{ paddingLeft: 3 }}
/>
</View>
)}
{props.selected && (
<>
<Appbar.Action
icon="delete"
onPress={() => props.onDeletePress()}
size={28}
color={colors.textColor}
style={{ left: 10 }}
/>
<Menu
ref={menu}
style={{ backgroundColor: colors.secondaryColor }}
onHidden={() => {
setShowMenu(false);
}}
button={
<Appbar.Action
icon="dots-vertical"
onPress={() => {
props.onActionPress().then((res) => {
console.log(res);
// if (res.status === 'TRUE') {
setShowMenu(res.status);
setActions(res.actions);
// } else {
// setShowMenu(false);
// }
});
}}
size={28}
color={colors.textColor}
/>
}>
{actions.length > 0 &&
actions.map((item, i) => {
return (
<>
{item.statusList !== undefined && (
<>
<MenuItem style={{ height: 40 }} disabled>
<Text style={{ color: colors.textLight }}>
{item.text}
</Text>
</MenuItem>
{item.statusList.map((list) => {
return (
<MenuItem
onPress={() => {
props.onMenuPress(item.type, list.VALUE);
menu.current.hide();
}}
key={i}>
<Text style={{ color: colors.textColor }}>
{' '}
{list.LABEL}
</Text>
</MenuItem>
);
})}
</>
)}
</>
);
})}
</Menu>
</>
)}
</Appbar.Header>
);
}
const mapStateToProps = (state) => {
var theme = getThemeColors(state.theme);
return { colors: theme };
};
export default connect(mapStateToProps, {
onSelectTheme: setTheme,
})(Header);
const styles = StyleSheet.create({
searchSection: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
searchIcon: {
padding: 10,
},
input: {
flex: 1,
height: 40,
padding: 10,
paddingLeft: 0,
backgroundColor: '#fff',
color: '#424242',
},
})
I don't know if I understood well but I need a little help.

UI not updating after adding or removing items with apollo cache and reactive variables

I'm having a hard time updating my UI after adding or removing items on my react native app with apollo cache.
To explain a little bit more. I have an
explorer Screen where are displayed some items with a toggle to subscribe or unsubscribe to an item. To another screen called "Subscribe Screen", I'm supposed to displayed all my favorite items. So I created a reactive variable called allFavoritesVar where I can add or delete items from there.
So in my cache.js I have :
import {InMemoryCache} from '#apollo/client/core';
import {makeVar} from '#apollo/client';
export const allFavoritesVar = makeVar([]);
export const cache = new InMemoryCache({
Query: {
fields: {
userFavorites: {
read() {
return allFavoritesVar();
},
},
}
}
})
So on my Explorer screen i'm checking if each item exist in allFavoritesVar to make the toggle red and inform the user that these items are already in their "Subscribe Screen".
const favExists = (flux) => {
if (allFavoritesVar().filter((item) => item.id === flux.id).length > 0) {
return true;
}
return false;
};
I was using redux before and switch to apollo because I needed to persist the cache when users were opening their app. Everything was much simpler with redux, the toggle were working fine and becoming red or grey when adding or removing items from the store and the "subscribe screen" was updating itself.
Now when I'm toggling, the mutation works, I can see that items are added or removed, but my ui is not updating. And when I'm closing my app, the last state of the cache is not displayed.
Here is my Explorer Screen :
import React, {useEffect, useState} from 'react';
import {
SafeAreaView,
StyleSheet,
Dimensions,
ScrollView,
TouchableOpacity,
Image,
FlatList,
ActivityIndicator,
} from 'react-native';
import {
NetworkStatus,
useLazyQuery,
useMutation,
useQuery,
} from '#apollo/client';
import {useSelector, useDispatch} from 'react-redux';
import {Box, Text} from 'react-native-design-utility';
import {Notifier} from 'react-native-notifier';
import {useTheme} from '#react-navigation/native';
import ErrorIcon from 'react-native-vector-icons/Ionicons';
import RefreshIcon from 'react-native-vector-icons/Ionicons';
import {theme} from '../theme/theme';
import Loading from '../components/Loading';
import CustomNotifier from '../components/CustomNotifier';
import CustomNotifierError from '../components/CustomNotifierError';
import SubscribeItem from '../components/SubscribeItem';
import {
SUBSCRIBE_FLUXGROUP_MUTATION,
SUBSCRIBE_FLUX_MUTATION,
UNSUBSCRIBE_FLUXGROUP_MUTATION,
UNSUBSCRIBE_FLUX_MUTATION,
} from '../graphql/mutations/fluxMutations';
import {
GET_EXPLORER_CATEGORIES_QUERY,
GET_EXPLORER_SLIDES_QUERY,
} from '../graphql/queries/explorerQueries';
import ToggleIcon from '../components/ToggleIcon';
import {HEIGHT} from '../utils/constants';
import {ALL_FAVORITES_QUERY} from '../graphql/queries/userQueries';
import {allFavoritesVar, cache} from '../utils/cache';
import {FLUX_QUERY} from '../graphql/queries/fluxesQueries';
const WIDTH = Dimensions.get('window').width;
const PAGE_SIZE = 10;
const ExplorerScreen = ({navigation}) => {
const {colors, dark} = useTheme();
const [limit, setLimit] = useState(PAGE_SIZE);
const [isError, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);
const {
data: explorerData,
loading: explorerLoading,
error,
refetch,
} = useQuery(GET_EXPLORER_CATEGORIES_QUERY, {
fetchPolicy: 'cache-first',
errorPolicy: 'all',
});
const {data: favoritesData, loading: favLoading} =
useQuery(ALL_FAVORITES_QUERY);
const {data: slidesData, loading: slidesLoading} = useQuery(
GET_EXPLORER_SLIDES_QUERY,
{
fetchPolicy: 'cache-first',
},
);
const [subscribeToFlux] = useMutation(SUBSCRIBE_FLUX_MUTATION);
const [subscribeToFluxGroup] = useMutation(SUBSCRIBE_FLUXGROUP_MUTATION);
const [unsubscribeFromFlux] = useMutation(UNSUBSCRIBE_FLUX_MUTATION);
const [unsubscribeFromFluxGroup] = useMutation(
UNSUBSCRIBE_FLUXGROUP_MUTATION,
);
const addFav = (flux) => {
const explorerFav = allFavoritesVar([...allFavoritesVar(), flux]);
console.log('explorerFav: ', explorerFav);
return explorerFav;
};
const favExists = (flux) => {
if (allFavoritesVar().filter((item) => item.id === flux.id).length > 0) {
return true;
}
return false;
};
const handleAddFavorite = async (flux) => {
if (flux.__typename === 'FluxGroup') {
addFav(flux);
Notifier.showNotification({
title: 'Vous êtes abonné à ce groupe de flux',
Component: CustomNotifier,
componentProps: {
alertType: 'info',
},
});
await subscribeToFluxGroup({
variables: {
id: parseInt(flux.id),
frequency: 'all',
},
});
} else {
addFav(flux);
Notifier.showNotification({
title: 'Vous êtes abonné à ce flux',
Component: CustomNotifier,
componentProps: {
alertType: 'info',
},
});
await subscribeToFlux({
variables: {
id: parseInt(flux.id),
frequency: 'all',
}
});
}
};
const handleRemoveFavorite = async (flux) => {
if (flux.__typename === 'FluxGroup') {
Notifier.showNotification({
title: 'Vous êtes désabonné de ce groupe de flux',
Component: CustomNotifierError,
componentProps: {
alertType: 'error',
},
});
await unsubscribeFromFluxGroup({
variables: {
id: parseInt(flux.id),
},
update: (cache, {data}) => {
const existingFavs = cache.readQuery({
query: ALL_FAVORITES_QUERY,
});
//console.log('DATA UPDATE:', data);
const newFavs = existingFavs.userFavorites.filter(
(item) => item.id !== flux.id,
);
console.log('DATA UPDATE:', newFavs);
cache.writeQuery({
query: ALL_FAVORITES_QUERY,
data: {userFavorites: [newFavs, ...existingFavs.userFavorites]},
});
},
});
} else {
Notifier.showNotification({
title: 'Vous êtes désabonné de ce flux',
Component: CustomNotifierError,
componentProps: {
alertType: 'error',
},
});
await unsubscribeFromFlux({
variables: {
id: parseInt(flux.id),
},
update: (cache, {data}) => {
const existingFavs = cache.readQuery({
query: ALL_FAVORITES_QUERY,
});
//console.log('DATA UPDATE:', data);
const newFavs = existingFavs.userFavorites.filter(
(item) => item.id !== flux.id,
);
console.log('DATA UPDATE:', newFavs);
cache.writeQuery({
query: ALL_FAVORITES_QUERY,
data: {userFavorites: [newFavs, ...existingFavs.userFavorites]},
});
},
});
}
};
function sliceIntoChunks(arr, chunkSize) {
const res = [];
for (let i = 0; i < arr.length; i += chunkSize) {
const chunk = arr.slice(i, i + chunkSize);
res.push(chunk);
}
return res;
}
useEffect(() => {
if (error) {
setIsLoading(true);
setError(error.message);
setIsLoading(false);
}
}, [error]);
const SeeMore = ({onPress}) => {
return (
<TouchableOpacity onPress={onPress}>
<Text
size={15}
mr="sm"
color={dark ? 'primary' : colors.text}
style={styles.letSpacing}>
Tout Voir
</Text>
</TouchableOpacity>
);
};
const renderHeader = () => {
if (slidesLoading) {
return (
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={{
paddingHorizontal: theme.space.sm,
paddingTop: theme.space.sm,
height: HEIGHT / 4.8,
justifyContent: 'center',
alignItems: 'center',
width: WIDTH,
}}>
<ActivityIndicator color={theme.color.primary} size={24} />
</ScrollView>
);
}
return (
<>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={{
paddingHorizontal: theme.space.sm,
paddingTop: theme.space.sm,
height: HEIGHT / 4.8,
}}>
{slidesData.explorer_slides.map((slide) => {
const type = slide.item.__typename;
return (
<TouchableOpacity
key={slide.id}
onPress={() =>
navigation.navigate(
type === 'Flux'
? 'SingleFlux'
: type === 'FluxGroup'
? 'MultipleFlux'
: 'FluxCategory',
{
headerTitle: slide.item.name,
headerItem: slide.item,
itemId: slide.item.id,
headerText:
slide.item.__typename !== 'FluxCategory'
? slide.item.description
: null,
},
)
}>
<Box
mx="xs"
bg="primary"
w={WIDTH - 120}
h={150}
radius="sm"
align="center"
justify="center"
overflow="hidden">
<Image
source={{uri: slide.image.uri}}
style={styles.imgCat}
resizeMode="cover"
/>
</Box>
</TouchableOpacity>
);
})}
</ScrollView>
<Box mt="md" h={1} w={WIDTH} bg={dark ? 'grey' : 'lightBorder'} />
</>
);
};
const renderItem = ({item, index}) => {
return (
<Box key={item - index} mb={8}>
{item.map((section, index) => {
const multiple = section.__typename === 'FluxGroup';
const subscribed = section.subscribed;
return (
<TouchableOpacity
key={section.id}
onPress={() =>
!multiple
? navigation.navigate('SingleFlux', {
headerTitle: section.name,
itemId: section.id,
headerItem: section,
subscribed: subscribed,
itemExist: exists(section),
})
: navigation.navigate('MultipleFlux', {
headerTitle: section.name,
itemId: section.id,
headerItem: section,
subscribed: subscribed,
itemExist: exists(section),
})
}>
<SubscribeItem
flux={section}
id={section.id}
channel={section.name}
title={
section.description
? section.description
: `Toutes les actualités sur ${section.name}`
}
icon={section.image?.uri ? `${section.image?.uri}` : null}
custom={section.customChannel}
pushNumber={section.frequency_numbers_all}
multiple={multiple}
button={
<>
{/* <ToggleIcon
favorite={exists(section)}
onPress={() =>
exists(section)
? handleRemoveFavorite(section)
: handleAddFavorite(section)
}
/> */}
<ToggleIcon
favorite={favExists(section)}
onPress={() =>
favExists(section)
? handleRemoveFavorite(section)
: handleAddFavorite(section)
}
/>
</>
}
/>
</TouchableOpacity>
);
})}
</Box>
);
};
const renderCategories = () => {
if (!explorerData) {
return (
<Box py="sm">
<Text mb="sm" center color="lightGrey">
Catégories en chargement
</Text>
<Loading />
</Box>
);
}
if (explorerData) {
return explorerData.explorer_categories.map((section) => {
const sectionData = sliceIntoChunks(section.related, 3);
return (
<>
<Box
w={WIDTH}
key={section.id}
dir="row"
justify="between"
align="center">
<Text
size="xl"
pt="sm"
pb="2xs"
ml="sm"
color={dark ? 'white' : 'black'}
style={styles.header}>
{section.name}
</Text>
<SeeMore
onPress={() =>
navigation.navigate('FluxCategory', {
headerTitle: section.name,
headerItem: section,
itemId: section.id,
headerText: null,
})
}
/>
</Box>
<Box>
<FlatList
horizontal
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.contentContainerStyle}
data={section ? sectionData : []}
renderItem={renderItem}
extraData={favoritesData}
keyExtractor={(item, index) => item + index}
onEndReachedThreshold={0}
/>
<Box h={1} bg={dark ? 'grey' : 'lightBorder'} mb="sm" />
</Box>
</>
);
});
}
};
if (error) {
return (
<Box f={1} justify="center" align="center">
<Box mb="xs">
<ErrorIcon
name="cloud-offline-outline"
color={dark ? theme.color.lightGrey : 'grey'}
size={32}
/>
</Box>
<Text
size="md"
center
color={dark ? 'lightGrey' : 'grey'}
style={styles.letSpacing}>
Une erreur s'est produite
</Text>
<Text
size="sm"
color={dark ? 'lightGrey' : 'grey'}
style={styles.letSpacing}>
Réessayez plus tard
</Text>
<TouchableOpacity onPress={() => refetch()}>
<Box mt="sm">
<RefreshIcon name="refresh" size={24} color={theme.color.primary} />
</Box>
</TouchableOpacity>
</Box>
);
}
if (isLoading) {
return <Loading />;
}
return (
<SafeAreaView
style={[styles.container, {backgroundColor: colors.background}]}>
<ScrollView showsVerticalScrollIndicator={false}>
<Box>{renderHeader()}</Box>
{renderCategories()}
</ScrollView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
width: WIDTH,
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
searchBar: {
width: WIDTH,
backgroundColor: theme.color.secondary,
borderBottomColor: theme.color.secondary,
borderTopColor: theme.color.secondary,
},
inputBar: {
backgroundColor: theme.color.black,
borderRadius: theme.space.md,
},
header: {
fontFamily: 'System',
fontWeight: '700',
letterSpacing: 0,
},
icon: {
width: 25,
height: 25,
borderRadius: 6,
backgroundColor: theme.color.primary,
overflow: 'hidden',
},
iconNull: {
width: 25,
height: 25,
borderRadius: 6,
backgroundColor: theme.color.primary,
overflow: 'hidden',
},
imgCat: {
width: '100%',
height: 150,
},
letSpacing: {
letterSpacing: 0,
},
});
export default ExplorerScreen;
Am I missing something ? Or Am I doing it totally wrong haha ?
If you need more info on my code, don't hesitate to ask :)
Try this. The UI will get refreshed as soon as you delete the item or add item:
await unsubscribeFromFlux({
variables: {
id: parseInt(flux.id),
},
refetchQueries: GET_EXPLORER_SLIDES_QUERY
});
Well turns out, you can't persist a reactive variable! So I'm just refetching queries and updating my cache after a mutation :) All good now ! Thank you !

Change border color text input When its empty in react native

I want when text input is empty change border color to red with press button:
const post = () => {
let list = [];
if (homeAge === '') {
list.push('homeage')
}
}
<TextInput
style={[Styles.TextInput, { borderColor: list.includes('homeage') ? 'red' : '#006d41' }]}
onChangeText={(event) => homeAgeHandler(event)}
/>
<Button style={Styles.Button}
onPress={() => post()}>
<Text style={Styles.TextButton}>ثبت اطلاعات</Text>
</Button>
Use a useRef hook :
const ref=useRef(0);
const post = () => {
let list = [];
if (homeAge === '') {
list.push('homeage')
}
}
useEffect(()=>{
if(list.size==0&&ref.current)
{
ref.current.style.borderColor = "red";
}
},[list,ref]);
<TextInput ref={ref}
onChangeText={(event) => homeAgeHandler(event)}
/>
<Button style={Styles.Button}
onPress={() => post()}>
<Text style={Styles.TextButton}>ثبت اطلاعات</Text>
</Button>
Here is a simple example to validate text and change styling based on validation,
const App = () => {
const [text, setText] = useState("");
const [error, setError] = useState(false);
const validateText = () => {
if (text === "") {
setError(true);
} else {
setError(false);
}
};
return (
<View>
<TextInput style={[Styles.TextInput, { borderColor: error ? 'red' : '#006d41', borderWidth:'1px'}]}
onChangeText={setText}
/>
<Button style={Styles.Button}
onPress={validateText}>
<Text style={Styles.TextButton}>ثبت اطلاعات</Text>
</Button>
</View>
);
};
export default App;
TextInput empty:
TextInput not empty:
Use state instead.
Also, In the given example, you are trying to access the list which is the local variable of the post() method.
Here is the alternate solution:
export default function App() {
const [homeAge, setHomeAge] = useState('');
return (
<View style={styles.container}>
<TextInput
value={homeAge}
style={[
styles.textInput,
{ borderColor: !homeAge ? 'red' : '#006d41' },
]}
onChangeText={(text) => setHomeAge(text)}
/>
<Button title={'ثبت اطلاعات'} style={styles.button} onPress={() => {}} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
textInput: {
padding: 10,
borderWidth: 1,
},
});
Working example: Expo Snack