Can anyone please help me to figure out this. I am using Flatlist and Inviewport dependencies for a video player integrated list view. I am facing memory issues but works fine in Android devices. It just crashing on iOS (in high end Android devices with RAM 6 Gig works perfectly)
Error logs
VideoPlayer.js
import React, {Component} from 'react';
import { View, StyleSheet, Dimensions, StatusBar } from 'react-native';
import { Video } from 'expo-av';
import InViewPort from 'react-native-inviewport';
export default class VideoPlayer extends React.Component {
pauseVideo = () => {
if(this.video) {
this.video.pauseAsync();
}
}
stopVideo = () => {
if(this.video) {
this.video.stopAsync();
}
}
playVideo = () => {
if(this.video) {
this.video.playAsync();
}
}
handlePlaying = (isVisible) => {
isVisible ? this.playVideo() : this.stopVideo();
}
render() {
//console.log(this.props);
return (
<View style={styles.container}>
<InViewPort onChange={this.handlePlaying}>
<Video
ref={ref => {this.video = ref}}
source={{ uri: 'https://dubaistack.com/beat/'+this.props.url }}
rate={1.0}
volume={1.0}
isMuted={false}
resizeMode="contain"
isLooping={true}
shouldPlay
style={{ width: Dimensions.get('window').width, height: Dimensions.get('window').height - StatusBar.currentHeight, backgroundColor: 'black' }}
/>
</InViewPort>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center'
}
});
ShortVideos.js
import React, {useState} from 'react';
import {
View ,
ScrollView,
StyleSheet ,
Text,
Dimensions,
FlatList,
StatusBar
} from 'react-native';
import VideoPlayer from '../components/VideoPlayer';
function Item(item) {
return (
<VideoPlayer url={item.item.location}/>
);
}
function ShortVideos(props) {
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
fetch('https://dubaistack.com/beat/posts/get.php?lvid')
.then((response) => response.json())
.then((responseJson) => {
//console.log(responseJson);
setLoading(false);
setData(responseJson);
})
if(loading){
return(
<Text style={{color:'red'}}>Loading</Text>
);
}else{
return(
<FlatList
data={data}
renderItem={({item}) => <Item item={item} /> }
keyExtractor={item => item.id}
pagingEnabled
/>
);
}
}
const styles = StyleSheet.create({
item: {
height: Dimensions.get('window').height,
width: Dimensions.get('window').width,
backgroundColor: '#336699',
justifyContent: 'center',
alignItems: 'center'
},
title: {
fontSize: 32,
color: 'white',
},
})
export default ShortVideos;
To remove - this warning you need to use componentWillUnmount like this below and rebuild: -
class Home extends Component {
_isMounted = false;
constructor(props) {
super(props);
}
componentDidMount() {
this._isMounted = true;
-----
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
...
}
}
Related
I'm using redux toolkit to manage state and am finding that the components are not updating when changes are made to the state.
I do see though, that when I listen to changes in my redux state within a useEffect that it will trigger the callback, meaning it is aware that the redux state has changed but the component is not rerendering.
Reading other questions it seems like a lot of the issues were about mutating the state, but I do know that using redux tool kit allows you to write "mutable" code and this is handled by Immer. Not sure if this is an issue that applies here...
My Slice:
const initialState = {
watchlists: []
}
export const watchlistsSlice = createSlice({
name: 'watchlists',
initialState,
reducers: {
updateWatchlists: (state, { payload }) => {
state.watchlists = payload;
},
},
})
export const { updateWatchlists } = watchlistsSlice.actions;
export default watchlistsSlice.reducer;
This is the component that calls the function that changes the state:
import React from 'react';
import { StyleSheet, View, Image } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import Text from '../core/Text';
import Pressable from '../core/Pressable';
import { AddIcon } from '../core/Icons';
import { formatPrice, formatPercent } from '../../utils/formatNumber';
import { shortenLongText } from '../../utils/formatText';
import { updateWatchlists } from '../../redux/watchlistsSlice';
import { addToWatchlist } from '../../storage/watchlists';
export default function ListItem({data, theme, navigation, watchlistID }) {
const dispatch = useDispatch();
const { currency } = useSelector(state => state.userPreference)
const addCryptoToWatchlist = async () => {
if (watchlistID) {
addToWatchlist(watchlistID, {
slug: data.slug,
})
.then(result => dispatch(updateWatchlists(result)))
.catch(err => console.log(err))
} else {
console.log('not ready yet')
}
}
return (
<Pressable
onPress={() => navigation.navigate('CoinDetails', {
data,
})}
>
<View style={styles.searchResult}>
<View style={styles.nameContainer}>
<Image style={styles.image} source={{uri: data.logo}} />
<View style={{marginLeft: 15}}>
<Text type={"big"} size={18} theme={theme.text}>{data.symbol}</Text>
<Text type={"regular"} size={14} theme={theme.text} style={{paddingTop: 1}}>{shortenLongText(data.name,25)}</Text>
</View>
</View>
<View style={styles.rightContainer}>
<View style={styles.priceContainer}>
<Text type={"big"} theme={theme.text}>{formatPrice(data.price, currency)}</Text>
<Text type={"big"} theme={data.direction === 'up' ? theme.percent.up : theme.percent.down}>{formatPercent(data.percent_change_24h)}</Text>
</View>
<Pressable onPress={() => addCryptoToWatchlist()}>
<AddIcon
size={30}
/>
</Pressable>
</View>
</View>
</Pressable>
)
}
const styles = StyleSheet.create({
searchResult: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 30,
},
nameContainer : {
flexDirection: 'row',
alignItems: 'center',
},
rightContainer: {
flexDirection: 'row',
alignItems: 'center',
},
priceContainer: {
alignItems: 'flex-end',
marginRight: 20
},
image: {
width: 28,
height: 28,
}
})
This is one the components that I expect to rerender when the state changes:
The useEffect does get trigged so the component is recognizing a change in state, but the component does not rerender.
I dont know if this is insightful, but the data in the state is an array of objects and in this case a single property one of the objects is getting changed.
import React, { useState, useEffect } from 'react';
import { View, StyleSheet } from 'react-native';
import { useSelector } from 'react-redux';
import GetStarted from './GetStarted';
import AddCryptoCard from './AddCryptoCard';
import CardList from './CardList';
import Text from '../core/Text';
export default function WatchlistCardsSection({ setModalVisible, navigation }) {
const { theme } = useSelector(state => state.userPreference);
const { watchlists } = useSelector(state => state.watchlists)
const { cryptoData } = useSelector(state => state.cryptoData);
const [ watchlistCryptoDataLoaded, setWatchlistCryptoDataLoaded ] = useState(false);
const checkIfWatchlistDataLoaded = () => {
const watchlistNames = watchlists.map(watchlist => watchlist.name);
const checkIfLoaded = cryptoData.map(data => watchlistNames.some(name => data.tags.includes(name))).includes(true);
setWatchlistCryptoDataLoaded(checkIfLoaded);
}
useEffect(() => {
checkIfWatchlistDataLoaded();
},[cryptoData])
useEffect(() => console.log("watchlist updated"), [watchlists])
return (
watchlists &&
watchlists.length === 0 ?
<GetStarted
setModalVisible={setModalVisible}
/>
:
watchlists.filter(item => item.viewOnHome).map(watchlist => (
watchlist.data.length === 0 ?
<AddCryptoCard
key={watchlist.id}
id={watchlist.id}
name={watchlist.name}
navigation={navigation}
/>
:
watchlistCryptoDataLoaded ?
<View
key={watchlist.id}
style={styles.sectionContainer}
>
<Text style={{paddingLeft: 20}} type={'big'} size={24} theme={theme.text}>{watchlist.name}</Text>
<CardList
name={watchlist.name}
config={{type: 'price'}}
navigation={navigation}
/>
</View>
: null
))
)
}
const styles = StyleSheet.create({
sectionContainer: {
flex: 1,
marginTop: 25,
}
})
So I have a an image selector that when I press, it launches the phone image library and then when I select the image it is supposed to fill that image selector but every time i select an image, I get this error
[TypeError: undefined is not an object (evaluating 'iter[Symbol.iterator]')]
This is how my code looks like this is the App file
import React, {useState} from 'react'
import { Button, StyleSheet, Text, View } from 'react-native'
import Screen from './app/components/Screen'
import ImageInputList from './app/components/ImageInputList'
export default function App() {
const [imageUris, setImageUris] = useState();
const handleAdd = (uri) => {
setImageUris([...imageUris, uri])
}
const handleRemove = uri => {
setImageUris(imageUris.filter(imageUri => imageUri !== uri))
}
return (
<Screen>
<ImageInputList imageUris={imageUris} onAddImage={handleAdd} onRemoveImage={handleRemove} />
</Screen>
)
}
And then this is my 2nd component the "imageInputList"
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import ImageInput from './ImageInput'
export default function ImageInputList({imageUris = [], onRemoveImage, onAddImage}) {
return (
<View style={styles.container}>
{imageUris.map((uri) => (
<View key={uri} style={{marginRight: 10}}>
<ImageInput
imageUri={uri}
onChangeImage={() => onRemoveImage(uri)}
/>
</View>
))}
<ImageInput onChangeImage={(uri) => onAddImage(uri)} />
</View>
)
}
And lastly, this is the imageInput component
import React, {useEffect} from 'react'
import { Image, StyleSheet, TouchableWithoutFeedback, Alert, View } from 'react-native'
import {MaterialCommunityIcons} from "#expo/vector-icons"
import * as ImagePicker from "expo-image-picker"
export default function ImageInput({imageUri, onChangeImage}) {
useEffect(() => {
requestPermission();
}, [])
const requestPermission = async () => {
const {granted} = await ImagePicker.requestMediaLibraryPermissionsAsync();
if(!granted) alert("Allow this app to access your image library")
}
const handlePress = () => {
if(!imageUri) selectImage();
else Alert.alert("Delete",
"Are you sure you want to delete this image?",
[{text: "Yes", onPress: onChangeImage(null)}, {text: "No"},])
};
const selectImage = async () => {
try {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 0.5,
});
if(!result.cancelled) onChangeImage(result.uri);
}
catch (error) {
console.log("Something went wrong in your code", error)
}
}
return (
<TouchableWithoutFeedback onPress={handlePress}>
<View style={styles.container} >
{!imageUri && <MaterialCommunityIcons name="camera" size={40} color="grey" />}
{imageUri && <Image source={{uri: imageUri}} style={styles.image} />}
</View>
</TouchableWithoutFeedback>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: "#f1f1f1",
borderRadius: 15,
marginTop: 40,
alignItems: 'center',
justifyContent: 'center',
overflow: "hidden",
width: 100,
height: 100,
},
image: {
height: "100%",
width: "100%",
}
})
I know the issue has something to do with "handleAdd" or the "onChangeImage" part but I do not know exactly how to fix them. Thanks in Advance.
The problem is here:
const [imageUris, setImageUris] = useState();
You're trying to spread imageUris but it is not an array when it is first initialized.
Simply replace useState(); with useState([]);
I want to click the TouchableOpacity and set the state true so that will open. I am getting error. and how to align the button in center at the header? alignSelf is not working.
`
import React, {Component} from 'react';
import {
StyleSheet,
SafeAreaView,
View,
TouchableOpacity,
Text,
} from 'react-native';
import Menu from '../../src/components/menubar';
export default class SearchPage extends Component {
constructor(props) {
super(props);
this.state = {isMenubarDisplayed: false};
}
static navigationOptions = {
headerTitle: () => {
return (
<TouchableOpacity
onPress={()=> this.setState({isMenubarDisplayed: true})}>
<Icon name="search" size={20} color="#000" />
</TouchableOpacity>
);
},
headerTitleStyle: {
alignSelf: 'center',
flex: 1,
},
};
render() {
return (
<SafeAreaView style={styles.container}>
{this.state.isMenubarDisplayed ? (
<Menu />
) : null}
</SafeAreaView>
);
}
}`
You need to try this, expo-snack .
This is my below code for search.js ,
import * as React from 'react';
import { Text, View, StyleSheet,TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
import Menu from './menu';
import Icon from 'react-native-vector-icons/FontAwesome';
export default class Search extends React.Component {
constructor(props){
super(props);
this.state={
isMenubarDisplayed: false,
}
}
static navigationOptions = ({ navigation }) => {
return {
headerTitle: () => {
return (
<TouchableOpacity onPress={navigation.getParam('toggleMenu')}>
<Icon name="search" size={20} color="#000" />
</TouchableOpacity>
);
},
};
};
toggleMenu = () => {
this.setState({ isMenubarDisplayed: !this.state.isMenubarDisplayed})
}
renderMenu = () => (
<Menu />
)
componentDidMount(){
this.props.navigation.setParams({
toggleMenu: this.toggleMenu
});
}
render() {
return (
<View style={styles.container}>
{this.state.isMenubarDisplayed?this.renderMenu():<View></View>}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
Hope it helps. feel free for doubts.
This is all you need https://reactnavigation.org/docs/en/header-buttons.html#header-interaction-with-its-screen-component
static navigationOptions = ({ navigation }) => {
return {
headerTitle: () => {
return (
<View style={{ flex: 1, alignItems: 'center' }}>
<TouchableOpacity onPress={navigation.getParam('toggleMenu')}>
<Icon name="search" size={20} color="#000" />
</TouchableOpacity>
</View>
);
},
};
};
componentDidMount() {
this.props.navigation.setParams({ toggleMenu: this.toggleMenu });
}
toggleMenu = () => {
this.setState({isMenubarDisplayed: true});
}
I created a 3 page application with React native navigation. Admob ads are on the 3rd page. I want to try the same ad code on all three screens. If there is any idea in this matter, please share. Thank you.
For better understanding I give the following expo code.
import React, { Component } from 'react';
import {
WebView,
AppRegistry,
StyleSheet,
Text,
View,
Button,
Alert
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import ListComponent from './ListComponent';
class App extends Component {
static navigationOptions = {
title: 'App',
};
OpenSecondActivityFunction = () => {
this.props.navigation.navigate('Second');
};
render() {
return (
<View style={styles.container}>
<Button
onPress={this.OpenSecondActivityFunction}
title="Open Second Activity"
/>
</View>
);
}
}
class SecondActivity extends Component {
static navigationOptions = {
title: 'SecondActivity',
};
OpenThirdActivityFunction = data => {
this.props.navigation.navigate('Third');
};
render() {
return (
<View style={{ flex: 1 }}>
<ListComponent
OpenThirdActivityFunction={this.OpenThirdActivityFunction}
/>
</View>
);
}
}
class ThirdActivity extends Component {
static navigationOptions = {
title: 'ThirdSecondActivity',
};
render() {
return (
<View style={{ flex: 1 }}>
<Text>3</Text>
</View>
);
}
}
const ActivityProject = StackNavigator({
First: { screen: App },
Second: { screen: SecondActivity },
Third: { screen: ThirdActivity },
});
export default ActivityProject;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
Listcomponent.js
import React, { Component } from 'react';
import {
AppRegistry,
View,
Text,
FlatList,
ActivityIndicator,
} from 'react-native';
import { List, ListItem, SearchBar } from 'react-native-elements';
class ListComponents extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false,
};
}
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '98%',
backgroundColor: '#CED0CE',
marginLeft: '2%',
}}
/>
);
};
renderHeader = () => {
return <SearchBar placeholder="Type Here..." lightTheme round />;
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: '#CED0CE',
}}>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
return (
<List containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
data={[{ name: 1, coders: 2 }]}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name}`}
subtitle={item.coders}
containerStyle={{ borderBottomWidth: 0 }}
onPress={() => this.props.OpenThirdActivityFunction(item.coders)}
/>
)}
keyExtractor={item => item.coders}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
/>
</List>
);
}
}
export default ListComponents;
Something does not make sense with my code.
I am using React Native to create a app.
In that app I am using a Tab Navigator.
It works fine until I call this.setState which for some reason triggers a unwanted Tab change from one tab to the other.
Why would setState trigger a Tab change??
This is my code:
import React from 'react';
import { StyleSheet, Text, View, FlatList, TextInput, StatusBar, Button } from 'react-native';
import { TabNavigator } from 'react-navigation';
import { Constants } from 'expo'
import { purple, white } from './utils/colors'
const R = require('ramda')
function CustomStatusBar({ backgroundColor, ...props }){
return (
<View style={{backgroundColor, height: Constants.statusBarHeight}}>
<StatusBar translucent backgroundColor={backgroundColor} {...props} />
</View>
)
}
export default class App extends React.Component {
constructor(props){
super(props)
this.handleDeckTitle = this.handleDeckTitle.bind(this)
}
state = {
title: ''
}
renderItem = (sample) => {
console.log('renderItem', sample)
return <Text>SAMPLE DATA</Text>
}
handleDeckTitle(e){
console.log('handleDeckTitle')
console.log('e', e)
console.log('this.state', this.state)
this.setState((prevState, props) => ({
title: e
}));
}
submitDeckTitle(){
console.log('submitDeckTitle')
}
render() {
console.log('R', R)
const Decks = () => {
return (
<View>
<CustomStatusBar backgroundColor={purple} barStyle='light-content' />
<Text>Decks!</Text>
</View>
)
}
const NewDeck = () => {
return (
<View>
<CustomStatusBar backgroundColor={purple} barStyle='light-content' />
<Text>What is the title of your new deck?</Text>
<TextInput style = {styles.input} onChangeText={this.handleDeckTitle}/>
<Button onPress={this.submitDeckTitle} title="Submit" />
</View>
)
}
const Tabs = TabNavigator({
Decks: {
screen: Decks
},
'New Deck': {
screen: NewDeck
},
});
return (
<Tabs />
);
}
}
const styles = StyleSheet.create({
container: {
paddingTop: 23,
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
input: {
margin: 15,
height: 40,
borderColor: '#7a42f4',
borderWidth: 1
},
});
I don't see what is wrong with this code.
In fact I think it should just work normally but it does not.
It triggers a tab change when I call handleDeckTitle which then calls this.setState
Got it to work now.
I changed the part that calls setState to be be a Separate Component with its own state.
Here is the code:
import React from 'react';
import { StyleSheet, Text, View, FlatList, TextInput, StatusBar, Button } from 'react-native';
import { TabNavigator } from 'react-navigation';
import { Constants } from 'expo'
import { purple, white } from './utils/colors'
const R = require('ramda')
function CustomStatusBar({ backgroundColor, ...props }){
return (
<View style={{backgroundColor, height: Constants.statusBarHeight}}>
<StatusBar translucent backgroundColor={backgroundColor} {...props} />
</View>
)
}
const Decks = () => {
return (
<View>
<CustomStatusBar backgroundColor={purple} barStyle='light-content' />
<Text>Decks!</Text>
</View>
)
}
class NewDeck extends React.Component {
constructor(props){
super(props)
this.handleDeckTitle = this.handleDeckTitle.bind(this)
}
state = {
title: ''
}
handleDeckTitle(e){
console.log('handleDeckTitle')
console.log('e', e)
console.log('this.state', this.state)
this.setState((prevState, props) => ({
title: e
}));
}
render(){
return (
<View>
<CustomStatusBar backgroundColor={purple} barStyle='light-content' />
<Text>What is the title of your new deck?</Text>
<TextInput style = {styles.input} onChangeText={this.handleDeckTitle}/>
<Button onPress={this.submitDeckTitle} title="Submit" />
</View>
)
}
}
const Tabs = TabNavigator({
Decks: {
screen: Decks
},
'New Deck': {
screen: NewDeck
},
});
export default class App extends React.Component {
constructor(props){
super(props)
// this.handleDeckTitle = this.handleDeckTitle.bind(this)
}
state = {
title: ''
}
renderItem = (sample) => {
console.log('renderItem', sample)
return <Text>SAMPLE DATA</Text>
}
submitDeckTitle(){
console.log('submitDeckTitle')
}
render() {
console.log('R', R)
return (
<Tabs />
);
}
}
const styles = StyleSheet.create({
container: {
paddingTop: 23,
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
input: {
margin: 15,
height: 40,
borderColor: '#7a42f4',
borderWidth: 1
},
});