How to display products in Flatlist properly? - javascript

I am trying to display some fetched products in a FlatList, also to display more products when I reach the end of this FlatList, but I am getting this error: Invariant Violation: ScrollView child layout (["alignItems"]) must be applied through the contentContainerStyle prop.
When I change alignItems from style to contentContainerStyle the app closes without showing an error or alert.
If I remove the alignItems from container the app closes too
export default function NewProducts() {
const [products, setProducts] = useState([]);
const [isLoading, setisLoading] = useState(false);
const [startLimit, setStartLimit] = useState({ start: 0, limit: 10 });
const navigation = useNavigation();
useEffect(() => {
setisLoading(true);
getProducts();
}, [startLimit]);
const gotoProduct = (id) => {
navigation.push("product", { idProduct: id });
};
const getProducts = async () => {
const response = await getLastProductsApi(
startLimit.start,
startLimit.limit
);
console.log(response);
setProducts(response);
};
const renderItem = ({product}) => {
return (
<TouchableWithoutFeedback
key={product.id}
onPress={() => gotoProduct(product.id)}
>
<View style={styles.containerProduct}>
<View style={styles.product}>
<Image
style={styles.image}
source={{
uri: `${product.attributes.images.data[0].attributes.formats.small.url}`,
}}
/>
<Text style={styles.name} numberOfLines={1} ellipsizeMode="tail">
{product.attributes.title}
</Text>
</View>
</View>
</TouchableWithoutFeedback>
);
};
const renderFooter = () => {
return isLoading && <Loading text="Loading more products" size="medium" />;
};
const handleLoadMore = () => {
setStartLimit({
start: startLimit.start + 10,
limit: startLimit.limit + 10,
});
setisLoading(true);
};
return (
<FlatList
style={styles.container}
numColumns={2}
data={products}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
ListFooterComponent={renderFooter}
onEndReached={handleLoadMore}
onEndReachedThreshold={0}
/>
);
}
const styles = StyleSheet.create({
container: {
alignItems: "flex-start",
marginTop: 1,
},
containerProduct: {
padding: 3,
},
product: {
padding: 10,
backgroundColor: "#f0f0f0",
borderRadius: 20,
},
image: {
height: 150,
resizeMode: "contain",
borderRadius: 20,
},
name: {
marginTop: 15,
fontSize: 15,
},
});
Here is where I call this component
export default function Home() {
return (
<>
<StatusBarCustom
backgroundColor={colors.primary}
barStyle="light-content"
/>
<Search />
<View>
<Banner />
<NewProducts />
</View>
</>
);
}

When you use renderItem from FlatList you have to use item and not product: https://reactnative.dev/docs/flatlist#required-renderitem
You can replace
const renderItem = ({product}) => {
By
const renderItem = ({item}) => {

Related

render only 10 item in react native flatlist on each page, then the next 5 on pull to load more item

i want to do paging but i cant limit the item to 10. this code shows all the items.
these are not working as well -initialNumToRender,maxToRenderPerBatch,windowSize
<FlatList
data={DATA}
renderItem={({ item }) => (
<Item title={item.name} />
)}
keyExtractor={item => item.id}
ItemSeparatorComponent={ItemSeparatorView}
initialNumToRender={11}
maxToRenderPerBatch={5}
windowSize={2}
/>
Try this,
import React, {useState, useEffect} from 'react';
//import all the components we are going to use
import {
SafeAreaView,
View,
Text,
TouchableOpacity,
StyleSheet,
FlatList,
ActivityIndicator,
} from 'react-native';
const App = () => {
const [loading, setLoading] = useState(true);
const [dataSource, setDataSource] = useState([]);
const [offset, setOffset] = useState(1);
useEffect(() => getData(), []);
const getData = () => {
console.log('getData');
setLoading(true);
//Service to get the data from the server to render
fetch('https://aboutreact.herokuapp.com/getpost.php?offset='
+ offset)
//Sending the currect offset with get request
.then((response) => response.json())
.then((responseJson) => {
//Successful response
setOffset(offset + 1);
//Increasing the offset for the next API call
setDataSource([...dataSource, ...responseJson.results]);
setLoading(false);
})
.catch((error) => {
console.error(error);
});
};
const renderFooter = () => {
return (
//Footer View with Load More button
<View style={styles.footer}>
<TouchableOpacity
activeOpacity={0.9}
onPress={getData}
//On Click of button load more data
style={styles.loadMoreBtn}>
<Text style={styles.btnText}>Load More</Text>
{loading ? (
<ActivityIndicator
color="white"
style={{marginLeft: 8}} />
) : null}
</TouchableOpacity>
</View>
);
};
const ItemView = ({item}) => {
return (
// Flat List Item
<Text
style={styles.itemStyle}
onPress={() => getItem(item)}>
{item.id}
{'.'}
{item.title.toUpperCase()}
</Text>
);
};
const ItemSeparatorView = () => {
return (
// Flat List Item Separator
<View
style={{
height: 0.5,
width: '100%',
backgroundColor: '#C8C8C8',
}}
/>
);
};
const getItem = (item) => {
//Function for click on an item
alert('Id : ' + item.id + ' Title : ' + item.title);
};
return (
<SafeAreaView style={{flex: 1}}>
<View style={styles.container}>
<FlatList
data={dataSource}
keyExtractor={(item, index) => index.toString()}
ItemSeparatorComponent={ItemSeparatorView}
enableEmptySections={true}
renderItem={ItemView}
ListFooterComponent={renderFooter}
/>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
flex: 1,
},
footer: {
padding: 10,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
},
loadMoreBtn: {
padding: 10,
backgroundColor: '#800000',
borderRadius: 4,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
btnText: {
color: 'white',
fontSize: 15,
textAlign: 'center',
},
});
export default App;

ReactNative rerenders component when the user opens the page

I have two components the first where user can add a place to the favorites and the second is favorites component where user may see all his favorite places. When the user for the first time opens the favorites component everything works as expected: all the favorite places that user has already added to the favorites rendered. But if user go to the first component and add one more place and then go to the second component new place will not appear because component has already rendered and the state didn't changed because useEffect not triggered. Help me please what should I use in my FavouritePlaces component instead of useEffect to rerender this component every time when user open FavouritePlaces?
Component where user can add to favorites:
const ModalWindow = ({navigateToPlace, sendDataToParent, visible, marker}: HomeNavigationProps<"ModalWindow">) => {
const regex = /(<([^>]+)>)|( )|(&nbps)/ig;
const result = marker.description.replace(regex, '');
const [favKeys, setFavKeys] = useState([]);
const onDismiss = () => {
sendDataToParent(false)
}
const onNavigationTap = () => {
onDismiss();
navigateToPlace(true, marker.coordinates);
}
const getFavourites = async () => {
let keys = []
keys = await AsyncStorage.getAllKeys()
setFavKeys(keys);
}
const onHeartPress = async () => {
const jsonValue = JSON.stringify(marker)
try {
if (favKeys.includes(marker.id.toString())){
await AsyncStorage.removeItem(marker.id.toString())
await getFavourites();
} else {
await AsyncStorage.setItem(marker.id.toString(), jsonValue)
await getFavourites();
}
} catch (e) {
console.log('error in onHeartPress', e)
}
console.log('Done.')
//remove after test
try {
await AsyncStorage.removeItem('__react_native_storage_test')
} catch(e) {
// remove error
}
console.log('Done.')
}
return (
<Modal visible={visible} onDismiss={onDismiss} contentContainerStyle={styles.container}>
<IconButton
style={
styles.iconButton
}
icon="close"
color={Colors.black}
size={30}
onPress={() => onDismiss()}
/>
<Text
style={{fontStyle: "italic", fontSize: 20, alignSelf: "center", maxWidth: '75%'}}>{marker.title}
</Text>
<CustomCarousel {...{marker}} />
<ScrollView showsVerticalScrollIndicator={false} style={{marginTop: '3%', marginLeft: '3%', marginRight: '3%'}}>
<Text>{result}</Text>
</ScrollView>
<View style={{flexDirection: "row", justifyContent: "space-around", marginLeft: "3%", marginRight: "3%", marginBottom: "15%"}}>
<TouchableOpacity onPress={() => onNavigationTap()}>
<View style={{flexDirection: "row", alignItems: "center"}}>
<Ionicons size={height/20} name={'navigate-circle-outline'} />
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => onHeartPress()}>
{marker.id ?
<View style={{flexDirection: "row", alignItems: "center"}}>
{favKeys.includes(marker.id.toString()) ? <Ionicons size={height/20} name={'heart-dislike'} /> : <Ionicons size={height/20} name={'heart'} />}
</View> : undefined}
</TouchableOpacity>
</View>
</Modal>
);
}
export default ModalWindow;
My Favorite Places component:
const FavouritePlaces = ({navigation}: HomeNavigationProps<"FavouritePlaces">) => {
const [markers, setMarkers] = useState([]);
useEffect(() => {
const getFavourites = async () => {
let keys = []
try {
keys = await AsyncStorage.getAllKeys()
} catch (e) {
// read key error
}
let values
try {
let forDeletion = ['__react_native_storage_test', 'NAVIGATION_STATE_KEY-40.0.0'];
keys = keys.filter(item => !forDeletion.includes(item))
values = await AsyncStorage.multiGet(keys)
setMarkers(values)
} catch (e) {
// read error
}
}
getFavourites();
}, [])
const transition = (
<Transition.Together>
<Transition.Out type='fade'/>
<Transition.In type='fade'/>
</Transition.Together>
);
const list = useRef<TransitioningView>(null);
const theme = useTheme()
const width = (wWidth - theme.spacing.m * 3) / 2;
const [footerHeight, setFooterHeight] = useState(0);
return (
<Box flex={1} backgroundColor="background">
<StatusBar style="black" />
<Header
title="Избранные места"
left={{icon: 'menu', onPress: () => navigation.openDrawer()}}
right={{icon: 'shopping-bag', onPress: () => true}}
/>
<Box flex={1}>
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={{
paddingBottom: footerHeight,
}}>
<Transitioning.View ref={list} transition={transition} style={{}}>
{markers ?
<Box flexDirection='row' style={{justifyContent: "space-around"}}>
<Box>
{markers
.filter((_, i) => i % 2 === 0).map((currentMarker) => <Picture
key={currentMarker}
place={currentMarker}
width={width}
height={height}
/>)}
</Box>
<Box>
{markers
.filter((_, i) => i % 2 !== 0).map((currentMarker) => <Picture
key={currentMarker}
place={currentMarker}
width={width}
height={height}/>)}
</Box>
</Box> : undefined}
</Transitioning.View>
</ScrollView>
{/*<TopCurve footerHeight={footerHeight}/>*/}
<Box position='absolute' bottom={0} left={0} right={0} onLayout={({
nativeEvent: {
layout: {height},
}
}) => setFooterHeight(height)}>
</Box>
</Box>
</Box>
)
}
export default FavouritePlaces
Try this
useEffect(() => {
// ... Your code goes here
}, [navigation]);
this will render whenever update in navigate
I've found the solution. React navigation has hook useIsFocused, so what can we do is:
import { useIsFocused } from "#react-navigation/native";
const isFocused = useIsFocused();
useEffect(() => {
// ... Your code goes here
}, [isFocused]);
You can use React Context API to share the state across the screens.
Check out this Expo Snack I created.
import {
CompositeNavigationProp,
NavigationContainer,
NavigatorScreenParams,
} from '#react-navigation/native';
import {
createStackNavigator,
StackNavigationProp,
} from '#react-navigation/stack';
import * as React from 'react';
import {
Button,
FlatList,
ListRenderItem,
Text,
TextInput,
View,
} from 'react-native';
type MainStackParamsList = {
FavoritePlacesScreen: undefined;
};
type ModalStackParamsList = {
MainStack: NavigatorScreenParams<MainStackParamsList>;
AddFavoritePlacesModal: undefined;
};
type FavoritePlace = {
id: number;
name: string;
};
type FavoritePlacesContextValue = {
favoritePlaces: FavoritePlace[];
addNewFavoritePlace: (favoritePlace: FavoritePlace) => void;
removeFavoritePlace: (id: number) => void;
};
const FavoritePlacesContext = React.createContext<FavoritePlacesContextValue>({
favoritePlaces: [],
addNewFavoritePlace: () => {},
removeFavoritePlace: () => {},
});
const MainStack = createStackNavigator<MainStackParamsList>();
type FavoritePlacesScreenProps = {
navigation: CompositeNavigationProp<
StackNavigationProp<MainStackParamsList, 'FavoritePlacesScreen'>,
StackNavigationProp<ModalStackParamsList>
>;
};
const FavoritePlacesScreen = ({navigation}: FavoritePlacesScreenProps) => {
const {favoritePlaces, removeFavoritePlace} = React.useContext(
FavoritePlacesContext,
);
const renderItem = React.useCallback<ListRenderItem<FavoritePlace>>(
({item}) => {
return (
<View style={{height: 50, padding: 10, flexDirection: 'row'}}>
<Text style={{fontSize: 16}}>{item.name}</Text>
<Button onPress={() => removeFavoritePlace(item.id)} title="Remove" />
</View>
);
},
[removeFavoritePlace],
);
return (
<View style={{flex: 1}}>
<FlatList
data={favoritePlaces}
keyExtractor={(item) => String(item.id)}
renderItem={renderItem}
/>
<Button
onPress={() => {
navigation.navigate('AddFavoritePlacesModal');
}}
title="Add new favorite"
/>
</View>
);
};
const MainStackNavigator = () => {
return (
<MainStack.Navigator>
<MainStack.Screen
component={FavoritePlacesScreen}
name="FavoritePlacesScreen"
/>
</MainStack.Navigator>
);
};
type AddFavoritePlacesModalProps = {
navigation: StackNavigationProp<
ModalStackParamsList,
'AddFavoritePlacesModal'
>;
};
const AddFavoritePlacesModal = ({navigation}: AddFavoritePlacesModalProps) => {
const {addNewFavoritePlace} = React.useContext(FavoritePlacesContext);
const [favoritePlaceName, setFavoritePlaceName] = React.useState('');
const handleOnSave = React.useCallback(() => {
addNewFavoritePlace({
id: Date.now(),
name: favoritePlaceName,
});
navigation.goBack();
}, [addNewFavoritePlace, favoritePlaceName, navigation]);
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<View style={{borderRadius: 6, borderWidth: 1, borderColor: '#333'}}>
<TextInput
onChangeText={setFavoritePlaceName}
placeholder="Name your favorite place"
/>
</View>
<Button onPress={handleOnSave} title="Save" />
</View>
);
};
// Put the favorite places list screen and the add favorite place modal here.
// Then use FavoritePlacesContext.Provider to wrap ModalStack.Navigator in order
// for the context to be available on MainStack
const ModalStack = createStackNavigator<ModalStackParamsList>();
const ModalNavigator = () => {
const [favoritePlaces, setFavoritePlaces] = React.useState<FavoritePlace[]>(
[],
);
const addNewFavoritePlace = React.useCallback(
(favoritePlace: FavoritePlace) => {
setFavoritePlaces((prev) => [...prev, favoritePlace]);
},
[],
);
const removeFavoritePlace = React.useCallback((id: number) => {
setFavoritePlaces((prev) =>
prev.filter((favoritePlace) => favoritePlace.id !== id),
);
}, []);
return (
<FavoritePlacesContext.Provider
value={{
favoritePlaces,
addNewFavoritePlace,
removeFavoritePlace,
}}
>
<ModalStack.Navigator headerMode="none">
<ModalStack.Screen component={MainStackNavigator} name="MainStack" />
<ModalStack.Screen
component={AddFavoritePlacesModal}
name="AddFavoritePlacesModal"
options={{headerShown: false}}
/>
</ModalStack.Navigator>
</FavoritePlacesContext.Provider>
);
};
const App = () => {
return (
<NavigationContainer>
<ModalNavigator />
</NavigationContainer>
);
};
export default App;

Using react-native-track-player to play selected song from flatlist

I can not figure out how I can play selected track/song from flatlist, I already had a playerscreen.js where I can play songs from my array of songs. I want to achieve users searching for songs and play the song from the selected list from the flatlist. Below is my songlist.js
const Songlist = () => {
const [loading, setLoading] = useState(false);
const navigation = useNavigation();
renderFooter = () => {
if (!loading) return null
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: '#CED0CE',
}}
>
<ActivityIndicator animating size='large' />
</View>
)
}
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '5%',
}}
/>
)
}
return (
<View>
<FlatList
data={songs}
keyExtractor={item => item.id}
renderItem={({ item, index }) => (
<TouchableOpacity
onPress={() => navigation.navigate('Player')}
>
<View
style={{
flexDirection: 'row',
padding: 16,
alignItems: 'center',
}}
>
<Avatar
source={{ uri: item.artwork.thumbnail }}
size='giant'
style={{ marginRight: 16 }}
/>
<Text
category='s1'
style={{color: '#000'}}
>{`${item.title}`}</Text>
</View>
<Text
category='s2'
style={{color: '#999', marginHorizontal: 80, bottom: 20}}
>Genre: {`${item.genre}`}</Text>
</TouchableOpacity>
)}
ItemSeparatorComponent={renderSeparator}
ListFooterComponent={renderFooter}
/>
</View>
);
}
export default Songlist;
This is my initial playerscreen.js
export default function PlayerScreen() {
const navigation = useNavigation();
const scrollX = useRef(new Animated.Value(0)).current;
const slider = useRef(null);
const isPlayerReady = useRef(false);
const index = useRef(0);
const [songIndex, setSongIndex] = useState(0);
const isItFromUser = useRef(true);
// for tranlating the album art
const position = useRef(Animated.divide(scrollX, width)).current;
const playbackState = usePlaybackState();
useEffect(() => {
// position.addListener(({ value }) => {
// console.log(value);
// });
scrollX.addListener(({value}) => {
const val = Math.round(value / width);
setSongIndex(val);
});
TrackPlayer.setupPlayer().then(async () => {
// The player is ready to be used
console.log('Player ready');
// add the array of songs in the playlist
await TrackPlayer.reset();
await TrackPlayer.add(songs);
TrackPlayer.play();
isPlayerReady.current = true;
await TrackPlayer.updateOptions({
stopWithApp: false,
alwaysPauseOnInterruption: true,
capabilities: [
Capability.Play,
Capability.Pause,
Capability.SkipToNext,
Capability.SkipToPrevious,
],
});
//add listener on track change
TrackPlayer.addEventListener(Event.PlaybackTrackChanged, async (e) => {
console.log('song ended', e);
const trackId = (await TrackPlayer.getCurrentTrack()) - 1; //get the current id
console.log('track id', trackId, 'index', index.current);
if (trackId !== index.current) {
setSongIndex(trackId);
isItFromUser.current = false;
if (trackId > index.current) {
goNext();
} else {
goPrv();
}
setTimeout(() => {
isItFromUser.current = true;
}, 200);
}
// isPlayerReady.current = true;
});
//monitor intterupt when other apps start playing music
TrackPlayer.addEventListener(Event.RemoteDuck, (e) => {
// console.log(e);
if (e.paused) {
// if pause true we need to pause the music
TrackPlayer.pause();
} else {
TrackPlayer.play();
}
});
});
return () => {
scrollX.removeAllListeners();
TrackPlayer.destroy();
// exitPlayer();
};
}, []);
// change the song when index changes
useEffect(() => {
if (isPlayerReady.current && isItFromUser.current) {
TrackPlayer.skip(songs[songIndex].id)
.then((_) => {
console.log('changed track');
})
.catch((e) => console.log('error in changing track ', e));
}
index.current = songIndex;
}, [songIndex]);
const exitPlayer = async () => {
try {
await TrackPlayer.stop();
} catch (error) {
console.error('exitPlayer', error);
}
};
const goNext = async () => {
slider.current.scrollToOffset({
offset: (index.current + 1) * width,
});
await TrackPlayer.play();
};
const goPrv = async () => {
slider.current.scrollToOffset({
offset: (index.current - 1) * width,
});
await TrackPlayer.play();
};
const renderItem = ({index, item}) => {
return (
<Animated.View
style={{
alignItems: 'center',
width: width,
transform: [
{
translateX: Animated.multiply(
Animated.add(position, -index),
-100,
),
},
],
}}>
<Animated.Image
source={item.artwork}
style={{width: 320, height: 320, borderRadius: 5}}
/>
</Animated.View>
);
};
return (
<SafeAreaView style={styles.container}>
<SafeAreaView style={{height: 320}}>
<Animated.FlatList
ref={slider}
horizontal
pagingEnabled
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
data={songs}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {x: scrollX}}}],
{useNativeDriver: true},
)}
/>
</SafeAreaView>
<TouchableOpacity
style={styles.genreContainer}
onPress={() => navigation.navigate('Genre')}
>
<SimpleLineIcons name="playlist" size={20} color='#fff' />
<Text style={styles.genre}> Genre</Text>
</TouchableOpacity>
<View>
<Text style={styles.title}>{songs[songIndex].title}</Text>
<Text style={styles.artist}>{songs[songIndex].artist}</Text>
</View>
<SliderComp />
<Controller onNext={goNext} onPrv={goPrv} />
</SafeAreaView>
);
}
import React from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text, StatusBar, ScrollView, TouchableOpacity } from 'react-native';
import Sound from 'react-native-sound';
const MusicApp = () => {
const DATA = [
{
title: 'Advertising Local',
isRequire: true,
url: require('./resource/advertising.mp3'),
},
{
title: 'Advertising URL',
url:
'https://raw.githubusercontent.com/zmxv/react-native-sound-demo/master/advertising.mp3',
},
{
title: 'Hosana',
isRequire: true,
url: require('./Hosana.mp3'),
},
{
title: 'Play aac sound from remote URL',
url:
'https://raw.githubusercontent.com/zmxv/react-native-sound-demo/master/pew2.aac',
},
{
title: 'Frog wav sound from Local',
isRequire: true,
url: require('./resource/frog.wav'),
},
{
title: 'Frog wav sound from remote URL',
url:
'https://raw.githubusercontent.com/zmxv/react-native-sound-demo/master/frog.wav',
},
];
let sound;
const playSound =(item, id)=>{
console.log("the url is "+ item.url);
if(item.url !=1){
sound = new Sound(item.url, '', (error, _sound) => {
if (error) {
alert('error' + error.message);
return;
}
stopSound
sound.play(() => {
sound.release();
});
});
}else{
sound = new Sound(item.url, (error, _sound) => {
if (error) {
alert('error' + error.message);
return;
}
stopSound
sound.play(() => {
sound.release();
});
});
}
}
const stopSound =(index)=>{
sound.stop(() => {
console.log('Stop');
});
}
const renderItem = ({ item, index }) => (
<ScrollView style={{flex: 1}}>
<View style={styles.item}>
<Text style={styles.title}> {item.title} </Text>
<TouchableOpacity onPress={()=>playSound(item, index)} style={{padding: 10, backgroundColor: 'blue'}}><Text>Play</Text></TouchableOpacity>
<TouchableOpacity onPress={()=>stopSound(index)} style={{padding: 10, backgroundColor: 'red'}}><Text>Stop</Text></TouchableOpacity>
</View>
</ScrollView>
);
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 20,
},
});
export default MusicApp;
NB:
Do yarn add react-native-sound or npm install react-native-sound.
Secondly, there are two sources of music- Remote url and local file path, ensure you modify the local file path appropriately before running

New to react native, Buttons don't seem to work individually

I'm trying to get each button to activate and "switch-on" when pressed, and I've used some documentation to help me. However, now it is not switching on.
Code Adds Switches in a FlatList
The Data should look like this:
https://imgur.com/a/761PSjre
Also, feel free to judge my code to the darkest depths of hell. I'm trying to improve my code.
import React from 'react'
import {StyleSheet, View,Text, Switch, Button, Alert, ScrollView, FlatList, SafeAreaView} from 'react-native'
export default () => {
const DATA = [
{
index: 1,
title: 'Toggle Night Mode',
},
{
index: 2,
title: 'Remind me to take a break',
},
{
index: 3,
title: "Remind me when it's bedtime",
},
];
const [enabledSwitches, setEnabledSwitches] = React.useState(DATA.length);
const toggleSwitch = () => setEnabledSwitches(previousState => !previousState);
function Item({title, index}) {
return (
<View>
<Text style={styles.text}> {title} </Text>
<Switch
trackColor={{ false: "#767577", true: "#81b0ff" }}
thumbColor="#f5dd4b"
ios_backgroundColor="#3e3e3e"
value={enabledSwitches[index]}
onValueChange={() => toggleSwitch(switches => {
switches[index] = !switches[index];
return switches;
})}
/>
</View>
)
}
function Header(){
return(
<View style = {styles.header}>
<Text style={styles.headertext}>Settings</Text>
</View>
)
}
return (
<>
<View style = {styles.container}>
<FlatList
data = {DATA}
keyExtractor = {item => item.id}
renderItem = {({ item, index }) => <Item title={item.title} index={index} /> }
ListHeaderComponent = {Header()}
/>
</View>
<View>
<Button
title = "Clear Search History"
color = "#6fb6f0"
onPress = {() => Alert.alert('Food History Has Been Cleared!')}
/>
</View>
<View>
<Button
title = "Logout"
color = "#6fb6f0"
onPress = {() => Alert.alert('Successfully Logged Out!')}
/>
</View>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 20,
fontWeight: "300"
},
headertext: {
fontSize: 30,
fontWeight: "300"
},
header:{
flex:1,
justifyContent: 'center',
alignItems: 'center',
padding: 10,
backgroundColor: '#f5f5f5'
}
})
Try this way
import React from 'react'
import {StyleSheet, View,Text, Switch, Button, Alert, ScrollView, FlatList, SafeAreaView} from 'react-native'
export default () => {
// use data set in default state
const [data, setData] = React.useState([ {index: 1,title: 'Toggle Night Mode'},...]);
function toggleSwitch(value, index){
const newData = [...data];
const newData[index].isEnable = value;
setData(newData);
}
function Item({item, index}) {
return (
<View>
<Text style={styles.text}> {item.title} </Text> // use `title` here like this
<Switch
.....
value={item.isEnable || false} // change here
onValueChange={(value) => toggleSwitch(value, index) } // change here
/>
</View>
)
}
return (
<>
<View style = {styles.container}>
<FlatList
data = {data}
keyExtractor = {item => item.id}
renderItem = {({ item, index }) => <Item item={item} index={index} /> } // send `item` as prop
/>
</View>
</>
);
}

react-native loading a list of images from gallery using hooks,

Im using react native and hooks to make a view where i choose from taking a photo with my camera or open my cellphone gallery and chose any image i want, once i have a picture it should show the image in a list, so if i choose more than one picture it should show more than oneimage in the list.
const Seleccion = ({navigation}) => {
const [fileList, setFileList] = useState([]);
const state = useMemo(() => ({ fileList }), [fileList]);
const onSelectedImage = useCallback((image) => {
setFileList(fileList => {
const newDataImg = [...fileList];
const source = { uri: image.path };
const item = {
id: Date.now(),
url: source,
content: image.data
};
newDataImg.push(item);
return newDataImg;
});
}, [setFileList]);
const takePhotoFromCamera = useCallback(() => {
ImagePicker.openCamera({
width: 300,
height: 400,
}).then(image => {
onSelectedImage(image);
console.log(image);
});
}, [onSelectedImage]);
const choosePhotoFromLibrary = useCallback(() => {
ImagePicker.openPicker({
width: 300,
height: 400,
}).then(image => {
onSelectedImage(image);
console.log(image);
});
}, [onSelectedImage]);
const renderItem = useCallback(({ item, index }) => {
return (
<View>
<Image source={item.url} style={styles.itemImage} />
</View>
);
}, []);
return (
<View style={styles.container}>
<FlatList
data={fileList}
keyExtractor={(item, index) => index.toString()}
renderItem={renderItem}
extraData={state}
/>
<TouchableOpacity style={styles.viewData} onPress={takePhotoFromCamera}>
<Text>Foto</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.viewData} onPress={choosePhotoFromLibrary}>
<Text>galeria</Text>
</TouchableOpacity>
</View>
);
}
This was my dirty component in a sample, take a look at it:
import React, {useState, useEffect, Fragment} from 'react';
import Icon from 'react-native-vector-icons/FontAwesome5Pro';
import {
View,
Modal,
ScrollView,
Image,
TouchableOpacity,
Text,
} from 'react-native';
import ActionSheet from 'react-native-action-sheet';
import ImagePicker from 'react-native-image-crop-picker';
import ImageViewer from 'react-native-image-zoom-viewer';
import styles from '../../../../../../styles';
const BUTTONS = ['Gallery', 'Camera', 'Cancel'];
export default props => {
let {files, setFiles} = props;
const [ImageViwerIsVisible, showImageViwer] = useState(false);
let [viewingIndex, setViewingIndex] = useState(-1);
useEffect(() => {
viewingIndex !== -1 && showImageViwer(true);
}, [viewingIndex]);
useEffect(() => {
!ImageViwerIsVisible && setViewingIndex(-1);
}, [ImageViwerIsVisible]);
const showActionSheet = () =>
ActionSheet.showActionSheetWithOptions(
{
title: 'Choose photo location',
options: BUTTONS,
cancelButtonIndex: 2,
destructiveButtonIndex: 2,
tintColor: 'blue',
},
buttonIndex => {
switch (buttonIndex) {
case 0:
return selectFromGallery();
case 1:
return selectFromCamera();
default:
return 0;
}
},
);
const selectFromGallery = () =>
ImagePicker.openPicker({
multiple: true,
//cropping: true,
})
.then(image => {
setFiles([...files, image].flat());
})
.catch(e => {});
const selectFromCamera = () =>
ImagePicker.openCamera({
cropping: true,
includeExif: true,
})
.then(image => {
setFiles([...files, image]);
})
.catch(e => {});
return (
<Fragment>
{!!ImageViwerIsVisible && (
<Modal transparent={true} onRequestClose={() => showImageViwer(false)}>
<ImageViewer
imageUrls={files.map(f => ({url: f.path}))}
index={viewingIndex}
/>
</Modal>
)}
<View
style={{
flexDirection: 'row',
padding: 10,
backgroundColor: '#ececec',
marginBottom: 5,
}}>
<TouchableOpacity onPress={() => showActionSheet()}>
<Icon solid name="camera-retro" size={32} />
</TouchableOpacity>
{/* <Text>{JSON.stringify(files, null, 2)}</Text> */}
<ScrollView style={{flexDirection: 'row', flex: 1}} horizontal>
{files.map((file, index) => (
<TouchableOpacity
key={file.path}
onPress={() => setViewingIndex(index)}
onLongPress={() =>
ImagePicker.cleanSingle(file.path)
.then(() => setFiles(files.filter(f => f.path !== file.path)))
.catch(e => {})
}
style={{
marginRight: 5,
width: 50,
height: 50,
borderWidth: 1,
borderColor: '#000',
borderRadius: 5,
}}>
<Image
source={{uri: file.path}}
style={{width: '100%', height: '100%', borderRadius: 5}}
/>
</TouchableOpacity>
))}
</ScrollView>
</View>
</Fragment>
);
};
You need to place useCallBack as you were use in your code, in that time i was dummy about react-hooks.

Categories

Resources