I am learning React native and was trying to build an app. However, the app is stuck on a white screen and doesn't show anything nor gives any error. This code is going to render a flatlist from an array and will have a delete to swipe button on the right. I am not getting any errors though.
Message.js
import React, { useState } from 'react'
import {
View,
Text,
StyleSheet,
FlatList,
SafeAreaView,
StatusBar,
ItemSeparatorComponent,
Platform
} from 'react-native'
import ListItem from '../components/ListItem'
import ListItemSeparator from '../components/ListItemSeparator'
import DeleteSwipe from '../components/DeleteSwipe'
const messages = [
{
id: 1,
name: 'T1',
description: 'D2',
image: require('../assets/mosh.jpg')
},
{
id: 2,
name: 'T2',
description: 'D2',
image: require('../assets/mosh.jpg')
},
{
id: 3,
name: 'T3',
description: 'D3',
image: require('../assets/mosh.jpg')
},
{
id: 4,
name: 'T4',
description: 'D4',
image: require('../assets/mosh.jpg')
}
]
export default function Message () {
const [messages, setMessage] = useState(messages)
const handleDelete = messages => {
const newMessages = messages.filter(m => m.id != messages.id)
setMessage(newMessages)
}
return (
<SafeAreaView style={styles.screen}>
<FlatList
data={messages}
keyExtractor={messages => messages.id.toString()}
renderItem={({ item }) => (
<ListItem
name={item.name}
description={item.description}
image={item.image}
onPress={() => console.log('touched', item)}
renderRightActions={() => (
<DeleteSwipe onPress={() => handleDelete(item)} />
)}
/>
)}
ItemSeparatorComponent={ListItemSeparator}
/>
<FlatList />
</SafeAreaView>
)
}
const styles = StyleSheet.create({
screen: {
padding: Platform.OS === 'android' ? StatusBar.currentHeight : 0
}
})
DeleteSwipe.js
import React from 'react'
import { View, Text, StyleSheet, TouchableWithoutFeedback } from 'react-native'
import Swipeable from 'react-native-gesture-handler/Swipeable'
import { AntDesign } from '#expo/vector-icons'
const DeleteSwipe = props => {
const { renderRightActions } = props
return (
<TouchableWithoutFeedback onPress={console.log('delete it')}>
<View style={styles.container}>
<AntDesign name='delete' size={24} color='white' />
</View>
</TouchableWithoutFeedback>
)
}
const styles = StyleSheet.create({
container: {
width: 70,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#ff5252'
}
})
export default DeleteSwipe
If it is a debugger issue, Restart the debugger if react native debugger is running. and again start
Or it could be a flexbox issue, Try to add or remove display: flex and flex:1 on this and parent.
Or it could be component is too small, Open inspector from dev menu and check component names in screen.
Change onPress={console.log....} to onPress={() => console.log(...
)}
But it seems what you want is onPress={props.onPress}
Related
I am using react-native-paper library to add a floating action button (FAB) which changes its width based on the scroll direction of the user.
What it's supposed to do -
If the user is scrolling upward expand the FAB instantly and contract on scrolling downward.
What's happening -
It is giving me the desired results but for some reason its take 3-4 seconds for the effect to take place.
Code -
import React from "react";
import { SafeAreaView, ScrollView } from "react-native";
import { AnimatedFAB } from "react-native-paper";
import Carousel from "../../components/carousel";
import Slider from "../../components/slider";
const HomePage = () => {
const [isExtended, setIsExtended] = React.useState(true);
function onScroll({ nativeEvent }: any) {
const currentScrollPosition =
Math.floor(nativeEvent?.contentOffset?.y) ?? 0;
setIsExtended(currentScrollPosition <= 0);
}
const categories = [
"Fruits",
"Cars",
"Places",
"Brands",
"Colors",
"Shapes",
"Sizes",
"Names",
];
return (
<SafeAreaView>
<ScrollView onScroll={onScroll}>
<Carousel />
{categories.map((item, index) => (
<Slider key={index} title={item} />
))}
</ScrollView>
<AnimatedFAB
style={{
position: "absolute",
bottom: 20,
right: 20,
}}
icon="filter-variant"
label="Filter"
animateFrom="right"
extended={isExtended}
onPress={() => console.log("Pressed")}
/>
</SafeAreaView>
);
};
export default HomePage;
Here is how the Sliders/Carousel looks [they share similar code]
import React from "react";
import { FlatList, StyleSheet, View, SafeAreaView } from "react-native";
import { Text } from "react-native-paper";
import data from "../../../data";
import Card from "../card";
const Slider = ({ title }: any) => {
const renderItem = ({ item }: any) => <Card item={item} />;
return (
<SafeAreaView>
<View style={styles.container}>
<Text style={styles.itemTitle}>{title}</Text>
<FlatList
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
horizontal
data={data}
renderItem={renderItem}
keyExtractor={(_, index) => index.toString()}
/>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
margin: 20,
},
itemTitle: {
color: "#fff",
marginBottom: 20,
fontSize: 20,
fontWeight: "500",
},
title: {
fontSize: 32,
},
});
export default Slider;
Solutions i tried -
I tried using the useEffect hook but didn’t notice a significant change.
I tried using the Flatlist component but the issue remains the same.
You can add scrollEventThrottle={16} prop to ScrollView or FlatList
Been using Expo and RN on an app, but I'm stuck with a problem. I'm using Expo Video
to make a photo/video gallery. The problem is, if I have more than 1 video in the array, only the last one will play (problem with useRef). And I couldn't find a way to create a ref for each one of them.
Solutions that I've tried and half worked: I created a VideoComponent (as a function then added on return), and each component had its own useRef and useState for playing inside the component a different useRef/useState for video/status for each. It worked okay-ish. But the problem was when other states changed (user presses like, for example). Whenever a state changes, and rerenders, the whole video reset to the beginning. Which is not ok.
The video reset on state change of other components doesn't affect the video if doing it normally (one useRef/state) but as I said, It's only playing the last component, which is not okay.
import React, { useRef, useState } from 'react';
import {
SafeAreaView,
View,
FlatList,
StyleSheet,
Text,
StatusBar,
} from 'react-native';
function App(props) {
const [allData, setAllData] = useState([
{
medias: [
{ link: 'https://link.com/link1.avi', mediaExtension: 'avi' },
{ link: 'https://link.com/link2.jpg', mediaExtension: 'jpg' },
{ link: 'https://link.com/link3.mov', mediaExtension: 'mov' },
],
name: 'Name',
description: 'description',
},
]);
const video = useRef(null);
const [status, setStatus] = useState({});
return (
<View style={{}}>
<FlatList
horizontal
data={allData}
renderItem={({ item }) => (
<View style={{}}>
{item.medias.map((item) => (
<View>
{item.mediaExtension === 'mov' || 'avi' || 'WebM' ? (
<View style={{ flex: 1 }}>
<TouchableOpacity
onPress={() =>
video.isPlaying
? video.current.pauseAsync()
: video.current.playAsync()
}>
<Video
ref={video}
style={{ alignSelf: 'center' }}
source={{
uri: item.link,
}}
onPlaybackStatusUpdate={(status) =>
setStatus(() => status)
}
/>
</TouchableOpacity>
</View>
) : (
<Image style={{}} source={{ uri: item.link }} />
)}
</View>
))}
</View>
)}
/>
</View>
);
}
export default App;
As far as I understand, you want to create a FlatList of Videos, and onScroll you want to pause it. This can be implemented as shown below
Also, here is a Working Example for this
import * as React from 'react';
import { Text, View, StyleSheet, FlatList } from 'react-native';
import Constants from 'expo-constants';
import VideoPlayer from './components/VideoPlayer';
const Videos = [
{
_id: 1,
source: require('./assets/videoplayback.mp4'),
},
{
_id: 2,
source: require('./assets/videoplayback.mp4'),
},
{
_id: 3,
source: require('./assets/videoplayback.mp4'),
},
{
_id: 4,
source: require('./assets/videoplayback.mp4'),
},
{
_id: 5,
source: require('./assets/videoplayback.mp4'),
},
{
_id: 6,
source: require('./assets/videoplayback.mp4'),
},
];
export default function App() {
const [Viewable, SetViewable] = React.useState([]);
const ref = React.useRef(null);
const onViewRef = React.useRef((viewableItems) => {
let Check = [];
for (var i = 0; i < viewableItems.viewableItems.length; i++) {
Check.push(viewableItems.viewableItems[i].item);
}
SetViewable(Check);
});
const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 80 });
return (
<View style={styles.container}>
<FlatList
data={Videos}
keyExtractor={(item) => item._id.toString()}
renderItem={({ item }) => <VideoPlayer {...item} viewable={Viewable} />}
ref={ref}
onViewableItemsChanged={onViewRef.current}
viewabilityConfig={viewConfigRef.current}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
},
});
and VideoPLayer component looks like this
import * as React from 'react';
import { Text, View, StyleSheet, Dimensions } from 'react-native';
import Constants from 'expo-constants';
import { Video, AVPlaybackStatus } from 'expo-av';
export default function VideoPlayer({ viewable, _id, source }) {
const video = React.useRef(null);
React.useEffect(() => {
if (viewable) {
if (viewable.length) {
if (viewable[0]._id === _id) {
video.current.playAsync();
} else {
video.current.pauseAsync();
}
} else {
video.current.pauseAsync();
}
} else {
video.current.pauseAsync();
}
}, [viewable]);
return (
<View style={styles.container}>
<Video
ref={video}
source={source}
rate={1.0}
volume={1.0}
resizeMode={'contain'}
isLooping
shouldPlay
style={styles.video}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
width: Dimensions.get('window').width,
marginBottom: 100,
marginTop: 100,
},
video: {
width: Dimensions.get('window').width,
height: 300,
},
});
I'm facing this issue
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
when I'm taping a character and delete it directly inside the searchbar. I'm getting the data from a json variable so everything is on the device. I'm not making any API calls.
I'm importing the data like this import foodData from "../config/foodData"; and it does look like this inside the foodData variable :
export default foodData = [
{
id: "10533",
title: "Agar Agar",
category: "Verschiedenes/Gelier- und Bindemittel",
kcal: "160",
protein: "2.4",
carbohydrates: "0",
fat: "0",
},
{
id: "10536",
title: "Agavensirup",
synonym: "Agavendicksaft",
category: "Süssigkeiten/Zucker und Süssstoffe",
kcal: "295",
protein: "0.2",
carbohydrates: "73",
fat: "0",
},
{
id: "1478",
...
I have about 900 objects in this tab.
Finally my component with the searchbar and the flatlist looks like this :
import React, { useState } from "react";
import {
View,
StyleSheet,
TextInput,
ScrollView,
FlatList,
TouchableHighlight,
} from "react-native";
import AppText from "../components/AppText";
import CardFood from "../components/CardFood";
import Screen from "../components/Screen";
import colors from "../config/colors";
import foodData from "../config/foodData";
import _ from "lodash";
function AddFoodScreen(props) {
const [data, setData] = useState([]);
const [fulldata, setFullData] = useState([foodData]);
const title = props.route.params.title;
const test = foodData.map((food) => {
return <AppText key={food.id}>{food.title}</AppText>;
});
const renderSeparator = () => (
<View
style={{
backgroundColor: "#3E405A",
height: 0.4,
width: "80%",
alignSelf: "center",
}}
/>
);
const handleSearch = _.debounce((text) => {
console.log(text);
const filteredData = foodData.filter((filteredFood) => {
if (filteredFood.title.toLowerCase().includes(text.toLowerCase())) {
return true;
} else if (
filteredFood.synonym &&
filteredFood.synonym.toLowerCase().includes(text.toLowerCase())
) {
return true;
} else {
return false;
}
});
setData(filteredData);
}, 500);
return (
<Screen>
<View style={styles.containerSearchBar}>
<TextInput
clearButtonMode={"while-editing"}
autoCorrect={false}
style={styles.searchInput}
placeholder="Nahrungsmittel suchen.."
placeholderTextColor="white"
onChangeText={(text) => handleSearch(text)}
/>
</View>
<View style={styles.container}>
<FlatList
contentContainerStyle={{ padding: 20 }}
data={data}
ItemSeparatorComponent={renderSeparator}
keyExtractor={(data) => data.id.toString()}
renderItem={({ item }) => {
return (
<CardFood
title={item.title}
kcal={item.kcal}
carbohydrates={item.carbohydrates}
protein={item.protein}
fat={item.fat}
style={{
width: "100%",
height: 100,
}}
onPress={() =>
props.navigation.navigate("FoodDetails", {
...item,
newTitle: title,
})
}
/>
);
}}
/>
</View>
</Screen>
I also used lodash in order to use a debounce in my handleSearch so that it doesnt record each and every character I tap inside de searchbar.
If somebody knows why this errror message occurs (why I have memoryleaks) would really safe my day.. Thanks a lot!
The below code is of Menu.js .I want to navigate to Settings.js by clicking on Settings .
I mentioned the code of Menu.js below.
Can I use the onPress to navigate to the next page wheni click on something?
I don't understand how to add this feature in the below code to the items.
import 'react-native-gesture-handler';
import React, { useState } from 'react';
import { Text, TextInput, View, StyleSheet ,TouchableOpacity,Image,Button,
ImageBackground,FlatList,StatusBar,SafeAreaView} from 'react-native';
const DATA = [
{
id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
title: "Tasks Done",
},
{
id: "3ac68afc-c605-48d3-a4f8-fbd91aa97f63",
title: "Goals",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d72",
title: "Rank",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d74",
title: "Week Plan",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d73",
title: "Settings",
},
];
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.item, style]}>
<Text style={styles.title}>{item.title}</Text>
</TouchableOpacity>
);
const Menu = () => {
const [selectedId, setSelectedId] = useState(null);
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#6e3b6e" : "#f9c2ff";
return (
<Item
item={item}
onPress={() => setSelectedId(item.id)}
style={{ backgroundColor }}
/>
);
};
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
item: {
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 25,
},
});
export default Menu;
I want to navigate to Settings.js as soon as I click on settings .
But I don't want to lose existing function of color changing .
How do I do this??
First of all, it is worth looking at the official docs here: https://reactnative.dev/docs/navigation
Official docs recommend react navigation, have a look at react navigation: https://reactnavigation.org/
Integrate this navigation in your project, once you have this you'd be able to user the navigation.navigate(nameOfYourView).
I was following tutorial and I got 'ReferenceError can't find variable flatlist' error, then I deleted the code and copied from the github to check if I might missed something and it's still didn't work.
import React, {useState} from 'react';
import {StyleSheet, Text, View,FlatList} from 'react-native';
import Header from './components/header';
export default function App () {
const [todos, setTodos] = useState([
{ text: 'buy coffee', key: '1' },
{ text: 'create an app', key: '2' },
{ text: 'play on the switch', key: '3' }
]);
return (
<View style={styles.container}>
<Header />
<View style={styles.content}>
{/* add todo form */}
<View style={styles.list}>
<FlatList
data={todos}
renderItem={({item}) => (
<Text>{item.text}</Text>
)}
/>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
content:{
padding: 40,
},
list:{
marginTop:20,
}
});
Header :
import React from 'react';
import {StyleSheet, Text, View} from 'react-native';
export default function Header() {
return(
<View style={styles.header}>
<Text style={styles.title}>My Todo List</Text>
</View>
)
}
const styles= StyleSheet.create({
header:{
height: 80,
paddingTop: 38,
backgroundColor: 'red',
},
title:{
textAlign: 'center',
color:'#fff',
fontSize: 20,
fontWeight:'bold',
}
});
export default Header;
Perhaps something changed in the version or something else. I will be grateful if you can tell how I can fix the error.
Edit: I changed the casing from Flatlist to FlatList and I got error 'Element type invalid: expected a string (for built- in components)'.
\ Screenshot
change case of Flatlist import as:
import {StyleSheet, Text, View,FlatList} from 'react-native';
check export of your header component. You have exported Header Component two time.
Try Replace Flatlist with FlatList
Correct: import {StyleSheet, Text, View,FlatList} from 'react-native';
Also, try to export as Class, if it's entry point:
import React, { Component } from 'react';
export default class App extends Component {
state = {
todos: [
{ text: 'buy coffee', key: '1' },
{ text: 'create an app', key: '2' },
{ text: 'play on the switch', key: '3' },
],
};
render() {
const { todos } = this.state;
return (
<View style={styles.container}>
<Header />
<View style={styles.content}>
{/* add todo form */}
<View style={styles.list}>
<FlatList
data={todos}
renderItem={({ item }) => <Text>{item.text}</Text>}
/>
</View>
</View>
</View>
);
}
}