Related
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,
},
})
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 !
I have this code below:
<Picker
prompt="Select Province"
selectedValue={province}
onValueChange={(itemValue, itemIndex) => {
setProvince(itemValue);
handleProvinceChange();
}}
style={styles.form}
>
<Picker.Item label="Select Province..." value="select" />
{dProvinces.map((provinceObj) => {
return (
<Picker.Item
key={provinceObj.ProvinceID}
label={provinceObj.Province}
value={provinceObj.Province}
/>
);
})}
</Picker>
Basically I loop through an object and assign labels and values to a Picker.Item. This is what the object looks like:
{
"ProvinceID": 140100000,
"Province": "Abra",
"CountryID": 142,
"RegionID": 0
},
{
"ProvinceID": 160200000,
"Province": "Agusan del Norte",
"CountryID": 142,
"RegionID": 0
},
Getting the value is find because onValueChange has a parameters that takes itemValue, but I want to get the key from the Picker.Item and put it as an argument on my handleProvinceChange(); function.
You can use the itemIndex and get the item in the array like below
onValueChange={(itemValue, itemIndex) => {
setProvince(itemValue);
const ProvinceID = dProvinces[itemIndex].ProvinceID;
handleProvinceChange();
}}
You can use item (index) and get the item in the array
import React, { useState } from "react";
import { View, Picker, StyleSheet } from "react-native";
const App = () => {
const [selectedValue, setSelectedValue] = useState("java");
return (
<View style={styles.container}>
<Picker
selectedValue={selectedValue}
style={{ height: 50, width: 150 }}
onValueChange={(itemValue, itemIndex) => setSelectedValue(itemValue)}
>
<Picker.Item label="Java" value="java" />
<Picker.Item label="JavaScript" value="js" />
</Picker>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 40,
alignItems: "center"
}
});
export default App;
I have a dropdown list once change a network call is fire and getting some value from server now i would like to dynamicall change the input value but onchange is not firing once i am setting the state after network call.
Input screen with render
<SearchableDropdown style={styles.inputBox}
onTextChange={this.onChangeText}
selectedItems={this.state.selectedItems}
onItemSelect={(item) => {
const items = this.state.selectedItems;
this.setState({ serviceid: JSON.stringify(item.id) });
this.getServiceCategories(JSON.stringify(item.id))
this.setState({
CategoryName:this.state.CategoryName
});
}}
containerStyle={{ padding: 5 }}
textInputStyle={{
padding: 12,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 15,
color: '#000',
width:300
}}
itemStyle={{
padding: 10,
marginTop: 2,
backgroundColor: '#ddd',
borderColor: '#fff',
borderWidth: 1,
borderRadius: 15,
color: '#000',
}}
itemTextStyle={{ color: '#000' }}
itemsContainerStyle={{ maxHeight: 240 }}
items={this.state.serviceData}
defaultIndex={0}
placeholder="Select Service"
name="serviceid"
resetValue={false}
underlineColorAndroid="transparent"
/>
<Field style={styles.inputBox}
name="categoryid"
value={this.state.CategoryName}
placeholder="Category"
returnKeyLabel={this.state.CategoryID}
component={this.renderTextInput}
/>
<Field style={styles.inputBox}
name="subcategoryid"
value={this.state.SubCategoryName}
returnKeyLabel={this.state.SubCategoryID}
placeholder="Sub Category"
component={this.renderTextInput}
/>
Renderinput
renderTextInput = (field) => {
const {meta: {touched, error}, label, secureTextEntry, maxLength,value, keyboardType, placeholder, input: {onChange, ...restInput}} = field;
return (
<View>
<InputText
onChangeText={onChange}
maxLength={maxLength}
keyboardType={keyboardType}
secureTextEntry={secureTextEntry}
label={label}
{...restInput} />
{(touched && error) && <Text style={styles.errorText}>{error}</Text>}
</View>
);
}
}
input component
class InputText extends Component<{}> {
state = {
value: ""
}
componentDidMount() {
this.setState({
value: this.props.value
});
}
onChangeText = (value) => {
this.setState({
value
}, () => {
this.props.onChangeText(value);
})
}
render() {
const {placeholder, secureTextEntry, keyboardType, maxLength, onChangeText, onSubmitEditing,value} = this.props;
return (
<View>
<TextInput
style={styles.inputBox}
underlineColorAndroid="rgba(0,0,0,0)"
placeholder={placeholder}
placeholderTextColor="rgba(255,255,255,0.8)"
selectionColor="#999999"
secureTextEntry={secureTextEntry}
keyboardType={keyboardType}
maxLength={maxLength}
returnKeyType="next"
value={this.state.value}
onSubmitEditing={onSubmitEditing}
onChangeText={this.onChangeText} />
</View>
);
}
}
Once dropdown change network api call
getServiceCategories =(value)=>{
fetch('http://localhost/api/getServiceCategories?serviceid='+value)
.then(response => { return response.json();})
.then(responseJson => {
this.setState({
CategoryID:responseJson[0].CategoryID,
SubCategoryID:responseJson[0].SubCategoryID,
CategoryName:responseJson[0].CategoryName,
SubCategoryName:responseJson[0].SubCategoryName,
});
})
.catch(error => {
console.error(error);
});
}
thanks
The problem is in your InputText component. This component is keeping the value inside its own state and sets the text input value from its state, rather than using the value from the props.
You have two choices: remove the state from the component and use only the value from the props - in this way your component will be controlled by the parent:
class InputText extends Component<{}> {
render() {
const {placeholder, secureTextEntry, keyboardType, maxLength, onChangeText, onSubmitEditing,value} = this.props;
return (
<View>
<TextInput
style={styles.inputBox}
underlineColorAndroid="rgba(0,0,0,0)"
placeholder={placeholder}
placeholderTextColor="rgba(255,255,255,0.8)"
selectionColor="#999999"
secureTextEntry={secureTextEntry}
keyboardType={keyboardType}
maxLength={maxLength}
returnKeyType="next"
value={value} // Use `value` from props
onSubmitEditing={onSubmitEditing}
onChangeText={onChangeText} /> // Use `onChangeText` from props
</View>
);
}
}
Or keep the state, and pass the value from its props to the TextInput and keep the state value as a fallback, in the case when the prop value is not set.
class InputText extends Component<{}> {
state = {
value: ""
}
componentDidMount() {
this.setState({
value: this.props.value
});
}
onChangeText = (value) => {
this.setState({
value
}, () => {
this.props.onChangeText(value);
})
}
render() {
const {placeholder, secureTextEntry, keyboardType, maxLength, onChangeText, onSubmitEditing,value} = this.props;
return (
<View>
<TextInput
style={styles.inputBox}
underlineColorAndroid="rgba(0,0,0,0)"
placeholder={placeholder}
placeholderTextColor="rgba(255,255,255,0.8)"
selectionColor="#999999"
secureTextEntry={secureTextEntry}
keyboardType={keyboardType}
maxLength={maxLength}
returnKeyType="next"
value={value || this.state.value} // Note that now the value is the value from the props and if it will be falsey, then the state value will be used as a fallback.
onSubmitEditing={onSubmitEditing}
onChangeText={this.onChangeText} />
</View>
);
}
}
I have Dynamic form in which user can add form and remove form when user start typing on first form TextInput it will give suggestion as per input. Now the problem is when user start typing on first TextInput field it will get suggestion but when user add another form by clicking on addForm Button and when user start typing on new form it will get suggestion but on same time in the first form it also start giving suggestion and same if there is three form it will start giving suggestion for all three form if user start typing in one form.I want to say that If user type any of form it will give suggestion on all form.
I want like if user is on first form then it will give suggestion only for first form not for second form as well. if user is on second form it will only get suggestion on second form not on first as well.
You can see in above picture it is giving suggestion for both form even if I'm typing on second form
import React, { PureComponent } from 'react'
import {
View,
TextInput,
ScrollView,
KeyboardAvoidingView,
StyleSheet,
Picker,
ListView,
FlatList
} from 'react-native'
import { getStockItems } from "../../actions/getIndentsAction";
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { CardSection, Text, Button, Block, Input } from '../../components';
import { theme } from '../../constants';
import { MaterialIcons,AntDesign,Entypo } from '#expo/vector-icons';
import { CardItem,Content, ListItem,Icon,Card, Left, Body, Right } from 'native-base';
class IndentForm extends PureComponent {
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
headerRight: (
<TouchableOpacity onPress={() => params.handleSave()}>
<AntDesign
name='plus'
style={{ paddingRight:10}}
size={25}
color='white'
/>
</TouchableOpacity>
)
};
};
constructor(props) {
super(props);
this.state = {
companyName:'',
formFields:[{
Item_Description:'',
Quantity:'',
}],
searchedForm:[]
};
}
componentDidMount() {
this.props.navigation.setParams({ handleSave: this.addInput});
this.props.getStockItems()
}
//add dynamic form
addInput = () => {
const existingFormFields = this.state.formFields.map(fields => ({...fields}))
const allFormFieldsAfterAddingNew = [...existingFormFields, {Item_Description: '', Quantity:''}]
this.setState({formFields: allFormFieldsAfterAddingNew})
}
//remove dynamic form
removeInput = index => () => {
this.setState({
formFields: this.state.formFields.filter((s, sidx) => index !== sidx)
});
};
//on Item Descriptionchange
onItemDescriptionChange = (text, index) => {
const { stocks } = this.props.indent;
const existingFormFields = this.state.formFields.map(fields => ({...fields}))
let targetField = {...existingFormFields[index]}
targetField.Item_Description = text
existingFormFields[index] = targetField
var searchedForm = stocks.filter(function(stock) {
return stock.Item.toLowerCase().indexOf(text.toLowerCase()) > -1;
});
this.setState({searchedForm: searchedForm , formFields: existingFormFields})
}
//on Quantity change
onQuantityChange = (text, index) => {
const existingFormFields = this.state.formFields.map(fields => ({...fields}))
let targetField = {...existingFormFields[index]}
targetField.Quantity = text
existingFormFields[index] = targetField
this.setState({formFields: existingFormFields})
}
itemSelect = (item,index) => {
const existingFormFields = this.state.formFields.map(fields => ({...fields}))
let targetField = {...existingFormFields[index]}
targetField.Item_Description = item.Item
existingFormFields[index] = targetField
this.setState({searchedForm:[], formFields:existingFormFields})
console.log("hello" + " " + item.Item + " " + index);
}
onsubmit = () => {
const data = {
companyName:this.state.companyName,
formFields:this.state.formFields
}
console.log(data)
}
render() {
const { stocks } = this.props.indent;
return (
<KeyboardAvoidingView style={{flex:1, justifyContent:"center"}} behavior="padding">
<ScrollView
showsVerticalScrollIndicator={false}
>
<Block padding={[5]}>
<Card>
<Picker
style={{flex:1}}
selectedValue={this.state.companyName}
onValueChange={(companyName)=>this.setState({companyName:companyName})}
>
<Picker.Item label='developer' value="0" />
<Picker.Item label="Developer" value="Developer" />
<Picker.Item label="Junior Develope" value="Junior Develope" />
</Picker>
</Card>
{
this.state.formFields.map((field, index) => {
return(
<Card key={index} >
<CardItem bordered>
<Left>
<Text bold>Items no : {index + 1}</Text>
</Left>
<Right>
<TouchableOpacity
onPress={this.removeInput(index)}
>
<Entypo
name="cross"
size={20}
color='#E46932'
/>
</TouchableOpacity>
</Right>
</CardItem>
<Block padding={[0, theme.sizes.base]}>
<Block>
<Input
label="description"
style={[styles.input]}
value={field.Item_Description}
onChangeText={(text)=> this.onItemDescriptionChange(text, index)}
/>
<FlatList
data={this.state.searchedForm}
keyExtractor={(ItemId,index) => index.toString()}
renderItem={({item,index})=>(
<ListItem
button={true}
key={index}
onPress={()=>this.itemSelect(item,index)}
>
<Text bold>{item.Item}</Text>
</ListItem>
)}
/>
</Block>
<Input
label="Quantity"
style={[styles.input]}
value={field.Quantity}
onChangeText={(text)=> this.onQuantityChange(text, index)}
/>
</Block>
</Card>
)
})
}
<Block padding={[0, theme.sizes.base * 1.5]}>
<Button
style={styles.submitInput}
onPress={this.onsubmit}>
<Text bold white center>Submit</Text>
</Button>
</Block>
</Block>
</ScrollView>
</KeyboardAvoidingView>
)
}
}
IndentForm.propTypes = {
getStockItems: PropTypes.func.isRequired,
indent: PropTypes.object.isRequired,
};
const mapStateToProps = state => ({
indent: state.indent,
errors:state.errors
});
export default connect(
mapStateToProps,
{
getStockItems,
}
)(IndentForm);
const styles = StyleSheet.create({
input: {
borderRadius: 0,
borderWidth: 0,
borderBottomColor: theme.colors.gray2,
borderBottomWidth: StyleSheet.hairlineWidth,
marginLeft:5
},
submitInput:{
backgroundColor:"#2ecc71"
},
addInput:{
backgroundColor:"white"
},
addButton:{
alignItems:"flex-end",
position:"absolute",
right:20,
bottom:20,
},
searchBarContainerStyle: {
marginBottom: 10,
flexDirection: "row",
height: 40,
shadowOpacity: 1.0,
shadowRadius: 5,
shadowOffset: {
width: 1,
height: 1
},
backgroundColor: "rgba(255,255,255,1)",
shadowColor: "#d3d3d3",
borderRadius: 10,
elevation: 3,
marginLeft: 10,
marginRight: 10
},
selectLabelTextStyle: {
color: "#000",
textAlign: "left",
width: "99%",
padding: 10,
flexDirection: "row"
},
placeHolderTextStyle: {
color: "#D3D3D3",
padding: 10,
textAlign: "left",
width: "99%",
flexDirection: "row"
},
dropDownImageStyle: {
marginLeft: 10,
width: 10,
height: 10,
alignSelf: "center"
},
pickerStyle: {
marginLeft: 18,
elevation:3,
paddingRight: 25,
marginRight: 10,
marginBottom: 2,
shadowOpacity: 1.0,
shadowOffset: {
width: 1,
height: 1
},
borderWidth:1,
shadowRadius: 10,
backgroundColor: "rgba(255,255,255,1)",
shadowColor: "#d3d3d3",
borderRadius: 5,
flexDirection: "row"
}
})
when you do addInput, it adds new FlatList for each input. but, data of FlatList is managed by single state which is this.state.searchedForm.
So whenever onItemDescriptionChange gets called, it updates the searchedForm state and all the FlatList reflects that change.
To resolve this, either you'll have to keep the FlatList data inside formFields state as one key or you can manage different state for each input.