I tried out the following example (Fading Hearts) from React Native Playground:
https://rnplay.org/apps/CkBOBQ
The example is written in createClass syntax. I converted the same to ES6 syntax where I used classes and methods. The app compiles and opens properly but when I tap on the app, it throws this error:
Undefined is not an object (evaluating '_this3.state.hearts[i].right')
There is also a warning:
Warning: setState(...): Cannot update during an existing state transition
Part of the code is shown below. If I remove the line onComplete={this.removeHeart(v.id)}, there is no error or warning. But this only because we are not properly destroying the heart object. Could someone point out what I'm doing wrong?
class App extends Component {
constructor(props) {
super(props);
this.state = {hearts: []}
this.addHeart = this.addHeart.bind(this);
this.removeHeart = this.removeHeart.bind(this);
}
addHeart() {
startCount += 1;
this.state.hearts.push({
id: startCount,
right: getRandomNumber(50, 150)
});
this.setState(this.state);
}
removeHeart(v) {
var index = this.state.hearts.findIndex((heart) => heart.id === v);
this.state.hearts.splice(index, 1);
this.setState(this.state);
}
render() {
return (
<View style={styles.container}>
<TouchableWithoutFeedback style={styles.container} onPress={this.addHeart}>
<View style={styles.container}>
{
this.state.hearts.map((v, i) =>
<AnimatedHeart
key={v.id}
onComplete={this.removeHeart(v.id)}
style={{right: this.state.hearts[i].right}}
/>
, this)
}
</View>
</TouchableWithoutFeedback>
<Text style={styles.message}>Tap anywhere to see hearts!</Text>
</View>
);
}
}
you are actually invoking the function with your current code. You need to give a function reference to trigger and not actually invoke the function. meaning
onComplete={this.removeHeart(v.id)}
should be this
onComplete={() => this.removeHeart(v.id)}
when the render cycle goes off it calls the removeHeart function which ends up calling setState while the render is happening cannot set state in a transition
You are passing argument to removeHeart in the wrong manner
<View style={styles.container}>
{
this.state.hearts.map((v, i) =>
<AnimatedHeart
key={v.id}
onComplete={this.removeHeart.bind(this,v.id)}
style={{right: this.state.hearts[i].right}}
/>)
}
</View>
and there is no need to pass this to map when you use the arrow function notation
Related
I started learning React Native 2 weeks ago and I got at the installing plugins chapter. I installed react-native-popover-view and I don't know but for me I get the error:
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
I searched around and after staying 2 days on this I understood that I have setState in render(), because I have the Modal in App render(). I tried to figure out how to change it, but without success.
So I have the Modal.js class:
class Modal extends Component {
state = {
isVisible: true
}
closePopover() {
this.setState({ isVisible: false });
}
render() {
return (
<View >
<Popover
isVisible={this.state.isVisible}
fromView={this.touchable}
onRequestClose={() => this.closePopover()}>
<View style={styles.closeXContainer}>
<Image source={closeX} style={styles.closeX} onPress={this.closePopover()} />
</View>
<Text>I'm the content of this popover!</Text>
</Popover>
</View>
);
}
}
and in the App.js, when the page loads, the modal should start first, covering the App.js component, that could be seen on the back.
class App extends Component {
render() {
return (
<View style={styles.container}>
<Modal />
</View>
);
}
}
Someone can please help with this? How the code should be to not get that error anymore?
This error is due to when you call onPress={this.closePopover()} on Image , it calls the function and causes the app to re-render which again causes function to be invoked, and hence an infinite loop. The best way would be calling it directly without parentheses onPress={this.closePopover} or create an anonymous function onPress={() => this.closePopover()}, which gets executed only once
your problem is in the call of the function this.closePopover() in order to fix it, you only need to change it to:
class Modal extends Component {
state = {
isVisible: true
}
closePopover() {
this.setState({ isVisible: false });
}
render() {
return (
<View >
<Popover
isVisible={this.state.isVisible}
fromView={this.touchable}
onRequestClose={this.closePopover}>
<View style={styles.closeXContainer}>
<Image source={closeX} style={styles.closeX} onPress={this.closePopover} />
</View>
<Text>I'm the content of this popover!</Text>
</Popover>
</View>
);
}
}
Hope this help.
I've really tried to read all over StackOverflow on this one since it's been asked many times before... but I can't find the solution to my error.
Invariant Violation: Objects are not valid as a React child (found: object with keys {id, title, desc, location, distance}).
I'm assuming there is something wrong in this file, not sure what thou...
PointOfInterestCluster.js
import React from 'react'
import { Text, View, TouchableOpacity,TouchableWithoutFeedback, ScrollView } from 'react-native'
import Styles from '../../constants/Styles';
export default class PointOfInterestCluster extends React.Component {
constructor(props) {
super(props)
this.state = {
data: this.props.data,
}
this.onMarkerPress = this.props.onMarkerPress.bind(this);
}
renderMarkers = (item, index) => {
console.log(item, index);
return (
<TouchableOpacity onPress={() => this.onMarkerPress(item)} key={'markerslist_'+index}>
<View style={[Styles.borderBottom, Styles.borderLight, Styles.padding]}>
<Text style={[Styles.text, Styles.textBold, Styles.textDark]}>{item.title}</Text>
</View>
</TouchableOpacity>
)
}
render() {
return (
<TouchableWithoutFeedback>
<View>
<ScrollView>
<Text style={[Styles.label, Styles.padding, Styles.borderBottom]}>
{this.props.data.length} enheter
</Text>
</ScrollView>
{this.state.data.map((item, index) => {this.renderMarker(item, index)})}
</View>
</TouchableWithoutFeedback>
);
}}
PointOfInterestCluster gets called liked this:
{this.state.currentCluster || this.state.currentMarker &&
<Overlay onClose={this.onCloseOverlay}>
{this.state.currentCluster &&
<PointOfInterestCluster onMarkerPress={this.onMarkerPress} data={this.state.currentCluster} poi={this.state.currentPOI}/>
}
{this.state.currentMarker &&
<PointOfInterest data={this.state.currentMarker} poi={this.state.currentPOI} />
}
</Overlay>
}
In the following line:
{this.state.data.map((item, index) => {this.renderMarker(item, index)})}
You're not really returning anything from the map function, because you have curly brackets around the this.renderMarker call. renderMarker gets called, but its result is being ignored.
You can change it to one of the following 3 alternatives which are all equivalent:
1.
1.
this.state.data.map((item, index) => {
return this.renderMarker(item, index);
});
2.
this.state.data.map((item, index) => this.renderMarker(item, index)})
3.
this.state.data.map(this.renderMarker)
If the problem persists, try printing the shape of your data and attaching it here.
Expected behaviour of this component is like this: I press it, selectedOpacity() function is called, state is updated so it now renders with opacity=1.
But for some reason, after calling this.setState, it is not being re-rendered. I have to click this component again to make it re-render and apply changes of opacity from state.
export default class Category extends Component {
state = {
opacity: 0.5
}
selectedOpacity() {
// some stuff
this.setState({opacity: 1})
}
render() {
return(
<TouchableOpacity style={[styles.container, {opacity: this.state.opacity}]} onPress={() => {
this.selectedOpacity();
}}>
</TouchableOpacity>
)
}
I think what you are missing is binding of selectedOpacity(), else this would be undefined in it AFAIK.
Also better move the assignment of state to a constructor().
constructor(props) {
super(props);
this.state = {};
this.selectedOpacity = this.selectedOpacity.bind(this);
}
Also change to the following because creating an arrow function inside render affects performance.
onPress={this.selectedOpacity}
Change selectedOpacity to arrow function:
selectedOpacity = () => {
this.setState({opacity: 1})
}
Then:
onPress={this.selectedOpacity}
Edit: The react documentation says its experimental and the syntax is called public class field syntax.
Try change onpress to
onPress={() => this.selectedOpacity()}
In my app i have a function outside 'export' which return a view, on click event of a button inside view another function is invoked which defined inside 'export'.
While clicking button error is received -
this4.myFunc is not a function.
screenshot attached below - error screen shot
below is my code -
export default class RosterView extends Component{
constructor(props){
super(props)
this.state={
....
}
render () {
mContext = this;
return (
<List dataArray={mContext.state.rosterList}
renderRow={(item) =>
<View style = {MainView.PlayerSelected}>
<AddPlayersToRosterList
data={item}/>
</View>
}>
</List>
);
}
isExist = (item) =>{
//My Code
return flag;
}
addRemovePlayer = (data) => {
//My Code
}
}
const AddPlayersToRosterList= ({data}) => (
(!mContext.isExist(data))?
<View style = {MainView.PlayerSelected}>
<Ripple style={MainView.PlayerAddRemoveContainer} onPress={()=> this.addRemovePlayer(data)}>
</Ripple>
</View>
)
when press button inside AddPlayersToList, this.addRemovePlayer() shows error.
please provide your help how to fix this problem.
Thanks in advance.
Hi you have many errors here. i tried to rewrite your code. try this here. and let me know if that helps. delete the whole AddPlayersToRosterList functional component that you wrote at the end. and rewrite it inside the component RosterView as a function.
The problem is, if you try to use functions that were not declared inside functional components, those components dont know the functions you are trying to call. In the case of const AddPlayersToRosterList you call the functions isExist(data) and this.addRemovePlayer(data), that were declared inside RosterView, but not inside AddPlayersToRosterList.
// add this function inside RosterView
AddPlayersToRosterList = (data) => {
(!this.isExist(data))?
<View style = {MainView.PlayerSelected}>
<Ripple
style={MainView.PlayerAddRemoveContainer}
onPress={() => this.addRemovePlayer(data)}>
</Ripple>
</View>
}
render (){
var mContext = this;
return (
<List dataArray={mContext.state.rosterList}
renderRow={(item) =>
<View style = {MainView.PlayerSelected}>
{this.AddPlayersToRosterList(item)// make sure you call the function here}
</View>
}>
</List>
);
}
you could just pass as props the functions you need to your functional component without delete anything. but it would be cost more memory and compution power that you probably need depending of how many props you want to pass.
const AddPlayersToRosterList= ({data, isExist, mContext, addRemovePlayer}) => (
(!mContext.isExist(data))?
<View style = {MainView.PlayerSelected}>
<Ripple
style={MainView.PlayerAddRemoveContainer}
onPress={()=> this.addRemovePlayer(data)}
>
</Ripple>
</View>
)
and in your render method
render () {
mContext = this;
return (
<List dataArray={mContext.state.rosterList}
renderRow={(item) =>
<View style = {MainView.PlayerSelected}>
<AddPlayersToRosterList
data={item}
isExist={this.isExist}
mContext={mContext}
addRemovePlayer={this.addRemovePlayer}
/>
</View>
}>
</List>
);
}
I'm a newbie in react native and I encountered the following problem while working on a sample chat app in android emulator.
Its showing the error
Undefined is not an object(evaluating 'this.state.contacts.filter')
in the line of code
let filteredContacts = this.state.contacts.filter(
(contact) => {
return contact.name.indexOf(this.state.search) !== -1;
}
);
Here is the code I'm working on.
componentDidMount() {
this.setState({contacts : this.props.navigation.state.params.contacts});
render() {
let filteredContacts = this.state.contacts.filter(
(contact) => {
return contact.name.indexOf(this.state.search) !== -1;
}
);
return (
<View style={styles.listContainer}>
<TextInput style={styles.searchbar} placeholder="Search"
onChange={(search) => {this.setState({search: search, isContactSearch: true}); this.onSearchPress}} />
{!this.state.isContactSearch &&
<FlatList
keyExtractor={(item, index) => index}
renderItem={this.renderFlatListItem}
data={this.state.historyContactList}
ItemSeparatorComponent={this.renderSeparator}>
</FlatList>
}
{this.state.isContactSearch &&
<FlatList>
{filteredContacts.map((contact) => {
return <View key={contact.number}>{contact.name}</View>
})}
</FlatList>
}
</View>
);
}
The componentDidMount callback runs only after the first render, so the state you set there isn't available on the first render pass.
You can move the state initialization logic to constructor instead:
constructor(props) {
super(props);
this.state = {
contacts: props.navigation.state.params.contacts
};
}
Note that here we set the state using regular javascript assignment instead of setState, because the latter is asynchronous and not safe to call in the constructor. If you want to later modify the state, you'll need to use setState as you have now.
Since componentDidMount is called after your render method your initial state value is not initialized! You can do 2 things here,
1) Set an initial value for your state params in the constructor if this is a one time setup you need!
2) If you want the state to update before every render you can copy the same code into componentWillMount/componentWillReceiveProps