I am attempting to convert the following code snippet to hooks. What I have converted so far, doesn't render output as the original. Below is the default code written as class components-
class Area extends React.PureComponent {
state = {
data: [],
tooltipX: null,
tooltipY: null,
tooltipIndex: null,
};
componentDidMount() {
this.reorderData();
}
reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
this.setState({
data: reorderedData,
});
};
render() {
const { data, tooltipX, tooltipY, tooltipIndex } = this.state;
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() =>
this.setState({
tooltipX: moment(item.date),
tooltipY: item.score,
tooltipIndex: index,
})
}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: '70%' }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: '#003F5A' }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid svg={{ stroke: 'rgba(151, 151, 151, 0.09)' }} belowChart={false} />
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: '50%',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 18,
color: '#ccc',
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
}
}
Below's the code I converted. I feel the below code is not rendering the tooltip because of how the state is written out in hooks. The tooltip isn't showing up upon click.
const Area = () => {
const [ data, setData ] = useState([]);
const [ tooltipX, setTooltipX ] = useState(null);
const [ tooltipY, setTooltipY ] = useState(null);
const [ tooltipIndex, setTooltipIndex ] = useState(null);
useEffect(() => reorderData(),[]);
const reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
setData(reorderedData);
};
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() =>
this.setState({
tooltipX: moment(item.date),
tooltipY: item.score,
tooltipIndex: index,
})
}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: '70%' }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: '#003F5A' }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid svg={{ stroke: 'rgba(151, 151, 151, 0.09)' }} belowChart={false} />
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: '50%',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 18,
color: '#ccc',
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
}
export default Area = () => {
const [data, setData] = useState([]);
const [tooltipX, setTooltipX] = useState(null);
const [tooltipY, setTooltipY] = useState(null);
const [tooltipIndex, setTooltipIndex] = useState(null);
useEffect(() => reorderData(), []);
const reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
setData(reorderedData);
};
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() => {
// try this 👇
setTooltipX(moment(item.date));
setTooltipY(item.score);
setTooltipIndex(index);
// try this ☝
}}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: "70%" }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: "#003F5A" }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid
svg={{ stroke: "rgba(151, 151, 151, 0.09)" }}
belowChart={false}
/>
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: "50%",
justifyContent: "center",
alignItems: "center",
}}
>
<Text
style={{
fontSize: 18,
color: "#ccc",
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
};
Related
I'm trying to delete selected image from an array of images and yet only the first image on the list gets deleted whenever i tap any other image to delete. This is surprising cause i implemented the same method when generating a list in the same screen and it deletes the correct list item each time i tap on it to delete.
Here is all the code in the file excluding cascading styles. Its a lot but i Hope this helps.
const AddPost = () => {
const navigation = useNavigation();
// Thumbnail State
const [thumbnail, setThumbnail] = useState(null);
// Media State
const [media, setMedia] = useState([]);
// Details State
const [title, setTitle] = useState('');
const [category, setCategory] = useState('');
const [caption, setCaption] = useState('');
const [price, setPrice] = useState('');
const [details, setDetails] = useState(false);
// Store State
const [itemName, setItemName] = useState('');
const [itemPrice, setItemPrice] = useState('');
const [photoArray, setPhotoArray] = useState([]);
const [storeItems, setStoreItems] = useState([]);
const width = useWindowDimensions().width;
const numColumns = Math.ceil(width / 120);
const pickThumbnail = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
quality: 0.5,
});
if (!result.cancelled) {
setThumbnail(result.uri);
}
};
const pickMedia = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
quality: 0.2,
});
if (!result.cancelled) {
setMedia([...media, result.uri]);
}
};
const deleteMedia = (index) => {
let removeMedia = [...media];
removeMedia.splice(index, 1);
setMedia(removeMedia)
console.log(index)
};
const addDetailItem = () => {
if (title == '' || category == '' || caption == '' || price == '') {
Alert.alert('Error', 'Please fill in the item detail.')
} else {
Keyboard.dismiss();
setDetails(true);
detailsRef?.current?.close();
};
};
const detailsRef = useRef(null);
const toggleModal = () => {
detailsRef?.current?.present();
}
const toggleModalClose = () => {
detailsRef?.current?.close();
}
const addItem = {
id: Math.floor(Math.random() * 11),
itemName: itemName,
itemPrice: itemPrice,
photo: photoArray
};
const storeRef = useRef(null);
const toggleStoreModal = () => {
storeRef?.current?.present();
}
const toggleStoreModalClose = () => {
storeRef?.current?.close();
}
const addStoreItem = () => {
if (itemName == '' || itemPrice == '') {
Alert.alert('Error', 'Please fill in the item detail.')
} else {
Keyboard.dismiss();
setStoreItems([...storeItems, addItem]);
setItemName(null);
setItemPrice(null);
setPhotoArray([]);
};
};
const deleteItem = (index) => {
let removeItem = [...storeItems];
removeItem.splice(index, 1);
setStoreItems(removeItem)
};
const pickPhoto = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
quality: 0.2,
});
if (!result.cancelled) {
setPhotoArray([...photoArray, result.uri]);
}
};
const deletePhoto = (index) => {
let removeItem = [...photoArray];
removeItem.splice(index, 1);
setPhotoArray(removeItem)
};
const store = () => {
setStoreItems([...storeItems]);
storeRef?.current?.close();
Keyboard.dismiss();
};
return (
<View style={styles.container}>
{/* Header */}
<Header
title={'Add Post'}
leftComponent={
<View style={styles.headerLeft}>
<Image source={images.logo} style={{ height: 28, width: 28, tintColor: COLORS.primary2}} />
</View>
}
rightComponent={
<TouchableOpacity
style={{
width: 50,
height: 50,
alignItems: 'center',
justifyContent: 'center',
}}
onPress= {() => console.log('Post Sumbitted') }
>
<Image
source={icons.post2}
style={{
width: 35,
height: 35,
tintColor: COLORS.primary2,
}}
/>
</TouchableOpacity>
}
/>
<View style={styles.subContainer}>
<ScrollView>
<Text style={styles.textDetail}>Add a photo or video post with thumbnail, category, caption, price, and store details (Optional).</Text>
{/* Add Thumbnail */}
<AddPostItem label={'Thumbnail'} onPress={pickThumbnail}/>
{thumbnail &&
<View style={styles.thumbnail}>
<Image source={{ uri: thumbnail }} style={styles.thumbnailPhoto} />
</View>
}
{/* Add Media */}
<AddPostItem label={'Media (Video/Photo)'} onPress={pickMedia} />
<View style={{flexDirection: 'row'}}>
{Array.from(Array(numColumns)).map((col, colIndex) => (
<View style={{flex: 1}} key={colIndex}>
{media
.filter((item, index) => index % numColumns === colIndex)
.map((item, index) => (
<View key={index.toString()} style={styles.mediaContainer} >
<Image source={{uri: item}} style={styles.addedMedia}/>
<TouchableOpacity style={styles.imageCancel} key={index} onPress={() => deleteMedia(index)}>
<Image source={icons.cross} style={styles.imageCancelIcon}/>
</TouchableOpacity>
</View>
))}
</View>
))
}
</View>
{/* Add Details */}
<AddPostItem label={'Details (Title, Caption, etc)'} onPress={toggleModal}/>
{details &&
<View style={{marginHorizontal: 20}}>
<View style={{flexDirection: 'row', }}>
<Text style={{color: COLORS.primary2, ...FONTS.body4}}>Title: </Text>
<Text style={{color: COLORS.white, ...FONTS.body4}}>{title}</Text>
</View>
<View style={{flexDirection: 'row', marginTop: 5,}}>
<Text style={{color: COLORS.primary2, ...FONTS.body4}}>Category: </Text>
<Text style={{color: COLORS.white, ...FONTS.body4}}>{category}</Text>
</View>
<View style={{flexDirection: 'row', marginTop: 5,}}>
<Text style={{color: COLORS.primary2, ...FONTS.body4}}>Price: </Text>
<Text style={{color: COLORS.white, ...FONTS.body4}}>N{price}</Text>
</View>
<View style={{marginTop: 5,}}>
<Text style={{color: COLORS.primary2, ...FONTS.body4}}>Caption: </Text>
<Text style={{color: COLORS.white, ...FONTS.body4}}>{caption}</Text>
</View>
</View>
}
{/* Add Store */}
<AddPostItem label={'Store Items (Optional)'} onPress={toggleStoreModal}/>
{storeItems.map((item, index) => {
return (
<View key={index.toString()} style={styles.itemContainer}>
<Text style={styles.itemName}>{item?.itemName}</Text>
<Text style={styles.itemPrice}>N{item?.itemPrice}</Text>
</View>
)
})
}
<View>
</View>
</ScrollView>
</View>
<BottomSheetModal
enablePanDownToClose={true}
ref={detailsRef}
snapPoints={['100%']}
index={0}
backgroundComponent={
({style}) =>
<View style={[style, {
backgroundColor: COLORS.primary,
borderRadius: 30,
}]}/>}
>
{/* Post Details */}
{/* Header */}
<ModalHeader left={toggleModalClose} right={addDetailItem} />
<View style={{flex: 1, backgroundColor: COLORS.primary3, borderTopRightRadius: 35, borderTopLeftRadius: 35}}>
<View style={styles.addDetailText}>
<Text style={styles.textHeader}>Add details</Text>
<Text style={styles.textBody}>Provide the necessary details for your post.</Text>
</View>
<View style={{paddingHorizontal: 10}}>
<FormInput
autoCapitalize='words'
placeholder={'Give it a title'}
onChange={setTitle}
value={title}
/>
<FormInput
autoCapitalize='words'
placeholder={'Category'}
onChange={setCategory}
value={category}
/>
<FormMultiText
autoCapitalize='sentences'
placeholder={'Caption'}
onChange={setCaption}
multiline={true}
numberOfLines={4}
value={caption}
/>
<FormInput
keyboardType='number-pad'
placeholder={'Price (Optional)'}
onChange={setPrice}
value={price}
/>
</View>
<Text style={{color: COLORS.gray, ...FONTS.body5, paddingHorizontal: 10, marginTop: 5, marginLeft: 65}}>Leave this input empty for a "FREE POST".</Text>
</View>
</BottomSheetModal>
<BottomSheetModal
enablePanDownToClose={false}
ref={storeRef}
snapPoints={['100%']}
index={0}
backgroundComponent={
({style}) =>
<View style={[style, {
backgroundColor: COLORS.primary,
borderRadius: 30,
}]}/>}
>
<ModalHeader left={toggleStoreModalClose} right={store} />
<View style={styles.storeItemConatainer}>
<View style={{flex: 1, backgroundColor: COLORS.primary3, borderTopRightRadius: 35, borderTopLeftRadius: 35}}>
<View >
<View style={styles.titleBar}>
<Text style={styles.textHeader}>Add store items (Optional)</Text>
</View>
<Text style={styles.textBody}>Include items available for purchase and their item price. You can add more than one item and multiple photo(s) to each item.</Text>
</View>
{/* Store Items */}
<TouchableOpacity onPress={pickPhoto} style={styles.addPhotoBtn}>
<Text style={{color: COLORS.primary, textAlign: 'center', ...FONTS.body3}}>Add Photo(s)</Text>
</TouchableOpacity>
{/* Photo Container */}
<View style={{flexDirection: 'row'}}>
{Array.from(Array(numColumns)).map((col, colIndex) => (
<View style={{flex: 1}} key={colIndex}>
{photoArray
.filter((item, index) => index % numColumns === colIndex)
.map((item, index) => (
<View key={index.toString()} style={styles.photoContainer}>
<Image source={{uri: item}} style={styles.addedPhoto}/>
<TouchableOpacity style={styles.photoCancel} onPress={() => deletePhoto(index)}>
<Image source={icons.cross} style={styles.photoCancelIcon}/>
</TouchableOpacity>
</View>
))}
</View>
))
}
</View>
<View style={styles.storeInput}>
<TouchableOpacity onPress={addStoreItem}>
<View style={styles.iconContainer}>
<Image source={icons.add} style={styles.icon}/>
</View>
</TouchableOpacity>
<View >
<FormStoreInput
autoCapitalize='words'
placeholder={'Item Name'}
onChange={setItemName}
value={itemName}
/>
<FormStoreInput
keyboardType='number-pad'
placeholder={'Item Price'}
onChange={setItemPrice}
value={itemPrice}
/>
</View>
</View>
<BottomSheetScrollView>
{storeItems.map((item, index) => {
return (
<View key={index.toString()} style={{flexDirection: 'row', marginBottom: 10, marginTop: 5}}>
<AddStoreItem items={item}/>
<TouchableOpacity style={styles.cancel} key={index} onPress={() => deleteItem(index)}>
<Image source={icons.cross} style={styles.cancelIcon}/>
</TouchableOpacity>
</View>
)
})
}
</BottomSheetScrollView>
</View>
</View>
</BottomSheetModal>
</View>
)
}
export default AddPost
This post has been updated.
I guess since we are dealing with images and not an object array that's why index wasnt working. So i changed index with items and decided to us the .filter method instead of .splice. And it worked.
const deleteMedia = (item) => {
setMedia((prev) => prev.filter((el) => el !== item));
console.log(item)
};
You need to declare remove media outside the function delete media because everytime you call delete media. You are reinitalizing the removeMedia useState to the original media values. I did not test these codes, but from the codes I can clearly tell that you are just reinitalizing remove media back to its original value. Try this
let removeMedia=[...media];
const deleteMedia = (index) => {
removeMedia.splice(index, 1);
setMedia(removeMedia)
};
Im working on a react-native project and what I'm trying to do is for the user to have the possibility to select phone numbers in his contact list.
When the user selects one or more contacts, the app won't work, and it shows this error on the console: VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc.
ContactList.js
unction ContactList() {
const [refreshing, setRefreshing] = React.useState(false);
const [itemChecked, setItemChecked] = useState([]);
const [checked, setChecked] = useState(new Map());
const [contacts, setContacts] = useState([]);
const [filter, setFilter] = useState([]);
const [search, setSearch] = useState('');
const [data, setData] = useState(filter)
useEffect(() => {
(async () => {
const { status } = await Contacts.requestPermissionsAsync();
if (status === 'granted') {
const { data } = await Contacts.getContactsAsync({
fields: [Contacts.Fields.PhoneNumbers],
// fields: [Contacts.Fields.Name],
});
if (data.length > 0) {
setContacts(data);
setFilter(data);
// console.log('contact', contacts[1]);
// console.log('filter', filter);
}
}
})();
}, []);
const searchFilter = (text) => {
if (text) {
const newData = contacts.filter((item) => {
const itemData = item.name ? item.name.toUpperCase() : ''.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setFilter(newData);
setSearch(text);
} else {
setFilter(contacts);
setSearch(text);
}
};
const onChangeValue = (item) => {
checked.set(item, true);
};
useEffect(() => {
checked &&
setData((previous) => [...previous, {phone: contacts} ])
}, [checked],
)
const renderItem = ({ item, index }) => {
return (
<SafeAreaView>
<ScrollView>
<TouchableOpacity style={{ flexDirection: 'row', flex: 1 }}>
<View style={{ flex: 1, borderTopWidth: 0.5, borderTopColor: 'grey', marginBottom: 15 }}>
<Text onPress={() => setChecked(true)} style={{ fontSize: 20, marginHorizontal: 10 }}>
{item.name + ' '}
</Text>
<Text style={{ fontSize: 17, marginHorizontal: 10, marginTop: 5, color: 'grey' }}>
{item.phoneNumbers && item.phoneNumbers[0] && item.phoneNumbers[0].number}
</Text>
</View>
<View style={{ flex: 1, borderTopWidth: 0.5, borderTopColor: 'grey' }}>
<CheckBox
style={{ width: 15, height: 15 }}
right={true}
checked={checked.get(index)}
onPress={()=> onChangeValue(index)}
/>
</View>
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
);
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<View
style={{
height: 40,
justifyContent: 'center',
backgroundColor: '#EEEEEE',
width: '90%',
marginHorizontal: 20,
marginTop: 15,
borderRadius: 10,
}}
>
<Feather name="search" size={20} color="grey" style={{ position: 'absolute', left: 32 }} />
<TextInput
placeholder="Search"
placeholderTextColor="#949494"
style={{
left: 20,
paddingHorizontal: 35,
fontSize: 20,
}}
value={search}
onChangeText={(text) => {
searchFilter(text);
setSearch(text);
}}
/>
</View>
<FlatList
style={{ marginTop: 15 }}
data={contacts && filter}
keyExtractor={(item) => `key-${item.id.toString()}`}
renderItem={renderItem}
ListEmptyComponent={<Text message="No contacts found." />}
/>
</View>
</SafeAreaView>
);
}
export default ContactList;
How can I solve this bug?
Not sure how I would convert this class component to hooks form. I tried to, but the app doesn't run the same.
Here's the original code written as class components-
class Area extends React.PureComponent {
state = {
data: [],
tooltipX: null,
tooltipY: null,
tooltipIndex: null,
};
componentDidMount() {
this.reorderData();
}
reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
this.setState({
data: reorderedData,
});
};
render() {
const { data, tooltipX, tooltipY, tooltipIndex } = this.state;
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() =>
this.setState({
tooltipX: moment(item.date),
tooltipY: item.score,
tooltipIndex: index,
})
}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: '70%' }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: '#003F5A' }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid svg={{ stroke: 'rgba(151, 151, 151, 0.09)' }} belowChart={false} />
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: '50%',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 18,
color: '#ccc',
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
}
}
This code is being used to integrate tooltip in react native charts. I want to include this code with rest of the project code written in hooks form.
export default class myPureComponent extends React.PureComponent
is equal to:
export default React.memo(()=>{
return <View>//...</view>
},(prevProps, nextProps) => {
return prevProps.x === nextProps.x;
// the component will be updated only on `x` prop changes.
})
componentDidMount is equal to:
React.useEffect(()=>reorderData(),[]);
Try to format your codes properly.
Add a [] in your use effects to it only render once.
useEffect(() => {
reorderData();
},[]); //add [] as a dependency, this renders useEffect only on mount.
Full codes
const Area = () => {
const [ data, setData ] = useState([]);
const [ tooltipX, setTooltipX ] = useState(null);
const [ tooltipY, setTooltipY ] = useState(null);
const [ tooltipIndex, setTooltipIndex ] = useState(null);
useEffect(() => reorderData(),[]);
const reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
setData(reorderedData);
};
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() =>
this.setState({
tooltipX: moment(item.date),
tooltipY: item.score,
tooltipIndex: index,
})
}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: '70%' }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: '#003F5A' }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid svg={{ stroke: 'rgba(151, 151, 151, 0.09)' }} belowChart={false} />
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: '50%',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 18,
color: '#ccc',
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
}
At my example, the function “getData” loading my data, but after the loading, I try to print and show the total sum of the objects that came from JSON in a footer at the bottom of the screen.
and I don't really know how to do it.
I don't understand how to solve this issue coz I have tried many ways.
This is my example:
export default class MainScreen extends Component {
constructor(props) {
super(props);
this.state = { data: [] };
}
getData = () => {
this.setState({ isLoading: true })
axios.get("https://rallycoding.herokuapp.com/api/music_albums")
.then(res => {
this.setState({
isLoading: false,
data: res.data
});
console.log(res.data);
});
}
componentDidMount() {
this.props.navigation.setParams({getData: this.getData}); //Here I set the function to parameter
this.getData()
}
renderItem(item) {
const { title, artist} = item.item;
return (
<TouchableOpacity
onPress={() => this.props.navigation.navigate("Settings")}
>
<Card
containerStyle={{
borderColor: "black",
padding: 20,
height: 100,
backgroundColor: "#e6e6ff",
borderBottomEndRadius: 10,
borderTopRightRadius: 10,
borderBottomStartRadius: 10,
}}
>
<View
style={{
paddingVertical: 15,
paddingHorizontal: 10,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center"
}}
>
<Icon name="chevron-right" size={30} color={"grey"} justifyContent={"space-between"} />
<Text style={styles.name}>
{title+ " " + artist}
</Text>
{/* <Text style={styles.vertical} numberOfLines={2}></Text> */}
</View>
</Card>
</TouchableOpacity>
);
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, paddingTop: 230 }}>
<Text
style={{ alignSelf: "center", fontWeight: "bold", fontSize: 20 }}
>
loading data...
</Text>
<ActivityIndicator size={'large'} color={'#08cbfc'} />
</View>
);
}
return (
<View style={styles.container}>
<FlatList
data={this.state.data}
renderItem={this.renderItem.bind(this)}
keyExtractor={item => item.id}
/>
</View>
);
}
}
/////////////////////////////////////////////////////////
MainScreen.navigationOptions = navData => {
return {
headerTitle: 'melon',
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title=**"sync button"**
iconName={Platform.OS === "android" ? "md-sync" : "ios-sync"}
onPress={() => {
navData.navigation.navigate("getData");
}}
/>
</HeaderButtons>
)
};
};
If type of data is array you can get total number of elements by this.state.data.length
If type of data is object you can get total number of elements by Object.keys(data).length
I have a custom iOS Picker Modal component and I want to add placeholder on it, is there anyway to do that? And my pickers are generated by map function, here is my picker code
class PickerWrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
type_absen: '',
modal: false
}
}
render() {
let picker;
let iosPickerModal = (
<Modal isVisible={this.state.modal} hideModalContentWhileAnimating={true} backdropColor={color.white} backdropOpacity={0.9} animationIn="zoomInDown" animationOut="zoomOutUp" animationInTiming={200} animationOutTiming={200} onBackButtonPress={() => this.setState({ modal: false })} onBackdropPress={() => this.setState({ modal: false })} >
<View style={{ backgroundColor: color.white, width: 0.9 * windowWidth(), height: 0.3 * windowHeight(), justifyContent: 'center' }}>
<Picker
selectedValue={this.state.type_absen}
onValueChange={(itemValue, itemIndex) => {
this.setState({ type_absen: itemValue });
this.setState({ modal: false });
setTimeout(() => this.props.onSelect(itemValue), 1200);
}}
>
{this.props.items.map((item, key) => <Picker.Item label={item.description} value={item.id} key={item.id} />)}
</Picker>
</View>
</Modal>);
if (Platform.OS === 'ios') {
var idx = this.props.items.findIndex(item => item.id === this.state.type_absen);
return (
<View style={this.props.style}>
{iosPickerModal}
<TouchableOpacity onPress={() => this.setState({ modal: true })}>
<View style={{ flexDirection: 'row', height: this.props.height ? this.props.height : normalize(40), width: this.props.width ? this.props.width : 0.68 * windowWidth(), borderWidth: 1, borderColor: color.blue, alignItems: 'center', borderRadius: 5 }}>
<Text style={{ fontSize: fontSize.regular, marginRight: 30 }}> {idx !== -1 ? this.props.items[idx].description : this.state.type_absen}</Text>
<IconWrapper name='md-arrow-dropdown' type='ionicon' color={color.light_grey} size={20} onPress={() => this.setState({ modal: true })} style={{ position: 'absolute', right: 10 }} />
</View>
</TouchableOpacity>
</View >);
}
}
unshift defualt value and label in the items array then check it in onValueChange
iosPickerModal = () => {
let makeitems = this.props.items;
// unshift defualt value and label
makeitems.unshift({
label: 'Please select an option...',
value: '-1',
});
return (
<Modal isVisible={this.state.modal} hideModalContentWhileAnimating={true} backdropColor={color.white} backdropOpacity={0.9} animationIn="zoomInDown" animationOut="zoomOutUp" animationInTiming={200} animationOutTiming={200} onBackButtonPress={() => this.setState({ modal: false })} onBackdropPress={() => this.setState({ modal: false })} >
<View style={{ backgroundColor: color.white, width: 0.9 * windowWidth(), height: 0.3 * windowHeight(), justifyContent: 'center' }}>
<Picker
selectedValue={this.state.type_absen}
onValueChange={(itemValue, itemIndex) => {
if (itemValue === '-1') return null;
this.setState({ type_absen: itemValue });
this.setState({ modal: false });
setTimeout(() => this.props.onSelect(itemValue), 1200);
}}
>
{makeitems.map((item, key) => <Picker.Item label={item.description} value={item.id} key={item.id} />)}
</Picker>
</View>
</Modal>)
};
render(){
if (Platform.OS === 'ios') {
var idx = this.props.items.findIndex(item => item.id === this.state.type_absen);
return (
<View style={this.props.style}>
{this.iosPickerModal}
<TouchableOpacity onPress={() => this.setState({ modal: true })}>
<View style={{ flexDirection: 'row', height: this.props.height ? this.props.height : normalize(40), width: this.props.width ? this.props.width : 0.68 * windowWidth(), borderWidth: 1, borderColor: color.blue, alignItems: 'center', borderRadius: 5 }}>
<Text style={{ fontSize: fontSize.regular, marginRight: 30 }}> {idx !== -1 ? this.props.items[idx].description : this.state.type_absen}</Text>
<IconWrapper name='md-arrow-dropdown' type='ionicon' color={color.light_grey} size={20} onPress={() => this.setState({ modal: true })} style={{ position: 'absolute', right: 10 }} />
</View>
</TouchableOpacity>
</View >);
}
}