Issue on Hide/Show component in React Native - reactjs

I am new in React native development and i am trying to do something simple. I have 4 components <TouchableOpacity> in a React Class extending Component.
In the render function, i am trying to hide three of these components while i am pressing on one.
render(){
[...]
return (
<TouchableOpacity style={styles.filterButtonAll} onPress={()=>this.toggleStatus()}>
<Ionicons size={15} name='XXX'> Show/Hide</Ionicons>
</TouchableOpacity>
<TouchableOpacity style={styles.filterButton}>
<Ionicons size={15} name='XXX'> Text1</Ionicons>
</TouchableOpacity>
<TouchableOpacity style={styles.filterButton}>
<Ionicons size={15} name='XXX'> Text2</Ionicons>
</TouchableOpacity>
<TouchableOpacity style={styles.filterButton}>
<Ionicons size={15} name='XXX'> Text3</Ionicons>
</TouchableOpacity>
[...]
)
}
toggleStatus function :
toggleStatus(){
this.state.showMenu = !this.state.showMenu;
};
And i have my showMenu in my state.
I tried conditional, and many other things following many posts about this subject, but when i am logging {this.state.showMenu} , it is dynamic in my toggleStatus() function but it's always the same value in my render function.
I think i am doing something wrong, thanks in advance for your help :)

The way your updating your state is incorrect. You have to call a function called setState() and then pass the updated state.
Reference for setState: https://reactjs.org/docs/react-component.html#setstate.
Example:
toggleStatus(){
this.setState({showMenu : !this.state.showMenu}); // RIGHT
// this.state.showMenu = !this.state.showMenu; // WRONG
};
Recommend way:
basically when your state update depend upon the previous state its better to use this approach.
toggleStatus(){
this.setState((state, props) => {
return {showMenu: !state.showMenu};
});
// this.state.showMenu = !this.state.showMenu; // WRONG
};

Related

onPress of TouchableOpacity gets called when loading Flatlist

I have a flatlist with a search function. In the next step, I want something to happen when I press within the item. I'm facing the problem that the TouchableOpacity is visible, but nothing happens when it's pressed. I've tried the same with a Button and face the same issue.
Instead of onPress being called when something is pressed, it somehow get's called once the screen loads immediately. If I for example console.log(item.title) I get all the titles in my console log that are currently in the flatlist.
I've tried to figure out what is causing this for many hours, but can't seem to find any reason. Any help is highly appreciated.
I have a Flatlist that is populated with data, set up as follows:
return (
<View style={styles.list}>
<FlatList
data = {this.state.data}
renderItem={renderListItem}
keyExtractor={item => item.id}
ListHeaderComponent={this.renderHeader}
/>
</View>
);
}
}
const renderListItem = (item) => {
return (
<MyTouchable
id={item.item.id}
title={item.item.title}
/>
);
}
MyTouchable Component looks like this:
<View>
<TouchableOpacity onPress={console.log("Pressed")}>
<View style={styles.mainView}>
<View>
<Text style={styles.text}>{props.id} {props.title}</Text>
</View>
</View>
</TouchableOpacity>
</View>
Try to pass the console.log inside an arrow function
<TouchableOpacity onPress={() => console.log("Pressed")}>
...
</TouchableOpacity>
You can’t write like this: <Component makeThing={myFunc()} />. You should write like that: <Component makeThing={() => myFunc()} /> if you are not using useCallback.
Make sure to read the docs about passing functions to components.
You must provide and arrow function, and call your function inside it.
Or just use useCallback hook:
const Component = () => {
const myFunction = useCallback(() => {
// do something
}, []);
return <AnotherComponent coolFunction={myFunction} />
}

TouchableOpacity onPress not working(emulator test)

So just getting started with react-native. I have a stateful component built as below :
class LoginView extends Component {
constructor(props) {
super(props);
this.state = {
inProgress: false
};
}
processLogin(){
this.setState({
inProgress: true
})
}
render() {
return (
<View style={styles.mainContainer}>
<View style={styles.loginContainer}>
<TextInput style={styles.input} placeholder="Password" />
<TouchableOpacity style={styles.button} onPress={() => this.processLogin()}>
{!this.state.inProgress && <Text style={styles.button_text}>Log in</Text>}/>}
</TouchableOpacity>
</View>
</View>
);}}
As you can see, i am binding the onPress event of the touchable opacity to the function processLogin. But for some reason, the function isn't fired!
The state doesn't change, also tried console.log but didn't fire either. Made sure that TouchableOpacity is imported from React-Native.
I am guessing that i messed up the binding. Any leads would be appreciated!
Note: Have only been trying this in the emulator, not in a real device yet.
instead of using the arrow function on onPress prop, use it when you are declaring the business function processLogin
So concretly, replace the declaration of this method by :
processLogin = () => {
this.setState({
inProgress : true
})
}
Then on onPress method, just change make it like this :
onPress = {this.processLogin}
Contrarily, you refer to the wrong this
Hope it's gonna help you sir
Regards.

React Native Component not re-rendering despite render function running

I am a little baffled here ..
I have some code like this
renderTopic(topic){
if(topic.isMine){console.log('work?')}
return (
<View key={topic.id.toString()} >
<TouchableOpacity onPress={() => this.addToic.call(this, topic)} >
{topic.isMine && <Topic topic={topic} active={true}/>}
{!topic.isMine && <Topic topic={topic} active={false}/>}
</TouchableOpacity>
</View>
)
}
render() {
console.log("re-rendering", this.props.myTopics)
return (
<View style={s.welcome}>
<ScrollView>
<View style={s.topics}>
{this.props.me.myTopics.map(topic => this.renderTopic(topic) )}
</View>
</ScrollView>
</View>
)
}
The problem is when I update topics using the traditional redux -> 'dispatch action' method, the re-render function fires, the should component update function fires, everything looks like it works but the topic.isMine conditional rendering never updates and switches the components over.
The correct result is displayed if I change page, the app then re-renders correctly.
Probably your reducer does not create new topic object. React sees, that reference to new topic matches old topic and skips calling renderTopic(topic) with same reference value as before.

undefined is not an object (evaluating 'this.onPress.bind')

Gist: Dashboard.js
I am creating an iOS application with React-Native, I get this error when my Dashboard.js is rendered.
My Dashboard.js renders different scrollableTabView's which each contain a ListView, I have included some code from one list item below.
renderArchiveOrder(order) {
return (
<TouchableHighlight onPress={this.onPress.bind(order.id)}>
<View style={styles.listObject}>
<View style={styles.thumbnail}>
<Text style={styles.title}>#{order.bid}</Text>
<Text style={styles.time}>TID</Text>
<Text style={styles.subTime}>{order.time}</Text>
</View>
<View style={styles.information}>
<Text style={styles.restaurantName}>{title}</Text>
<Text style={styles.dropoffName>{order.locations.dropoff.address}</Text>
</View>
</View>
</TouchableHighlight>
);
}
My problem is that when Dashboard.js is rendered, the app crashes and the included screenshot is being shown.
It clearly states that renderArchiveOrder: Dashboard.js # 364:0 is throwing an error, here's my onPress function.
I can't understand my error is line 364, and why my TouchableHighlight onPress doesn't work.
id: index is line 364.
onPress = (index) => {
this.props.navigator.push({
title: 'Order',
component: Order,
passProps: {
id: index
}
})
};
Why does this occur when I'm just trying to send data to a click function and push the navigator to a new view?
Since you're passing your renderArchiveOrder method as a value to another component, it loses its context. Thus, the this variable inside your method will not refer to your component instance, and trying to access this.onPress will yield undefined.
To fix this, simply bind your method to your component instance before passing it to your ListView component:
<ListView
dataSource={this.state.archiveDataSource}
renderRow={this.renderArchiveOrder.bind(this)}
style={styles.listView}/>

props data is not working in componentDidMount using react-native

I have been struggling for the last 24 hrs about this one.
The problem is that, I am passing props in my parent class and calling the props data in my child component. When I check in render method the data shows correctly, but when calling it in componentDidMount it shows null.
Here is the sample of my code
class parent extends React.Component{
constructor(props){
super(props);
this.state = {
isOpen: false,
day:'monday',
data:[],
data1:[]
}
}
back () {
this.props.navigator.pop();
}
Mon(){
var record=[];
this.state.data.map((data) => {
if(data.frequency[0].day == 'Monday'){
record.push(data);
}
});
this.setState({data1:record});
}
componentWillMount() {
this.fetchData();
}
fetchData() {
//here i am getting the data from service and set the data to the state
}
render(){
return(
<View style={styles.Maincontainer}>
<View style={styles.navbar}>
<View style={styles.navbarContainer}>
<View style={styles.navbarIcon}>
<TouchableOpacity style={styles.button} onPress={() => this.back()}>
<Image
style={styles.menu2}
source={require('image!nav_arrow')} />
<Text style={styles.navbuttonText}>Back</Text>
</TouchableOpacity>
</View>
<View style={styles.navbarText}>
<Text style={styles.title}>Title</Text>
</View>
<View style={styles.navbarButton}>
</View>
</View>
</View>
<View style={styles.subMainContainer}>
<View style={styles.weekHeader}>
<View style={styles.weekRow}>
<Text style={styles.text} onPress={this.Mon.bind(this)}>Mon</Text>
</View>
</View>
<View style={styles.listBody}>
{this.state.data1.length == 0 ?(
<List data={this.state.data} />
) : (
<List data={this.state.data1}/>
)}
</View>
</View>
</View>
);
}
}
In my child component, I want to display the data in a ListView which is taken from the parent component
class List extends React.Component{
constructor(props){
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
}),
isLoading: true
};
}
componentDidMount(){ //**here props data shows empty**
alert("didMount:"+JSON.stringify(this.props.data));
var data=this.props.data
this.setState({
dataSource: this.state.dataSource.cloneWithRows(data),
isLoading: false
});
}
render() { //**In this alert it shows correct data**
alert("data:"+JSON.stringify(this.props.data));
if (this.state.isLoading) {
return this.renderLoadingView();
}
return(
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderSchedule.bind(this)}
automaticallyAdjustContentInsets={false}/>
);
}
renderLoadingView(){
return(
<View style={styles.loading}>
<ActivityIndicatorIOS
size='large'/>
<Text>
Loading Schedule...
</Text>
</View>
);
}
renderSchedule(data){
return(
<View style={styles.ListContainer}>
<View style={styles.subContainer}>
<Text style={styles.classText}>Class Name: {data.name}</Text>
{data.frequency[0].day == null ? (
<Text style={styles.Day}>Day: {data.frequency[0].date}</Text>
):
<Text style={styles.Day}>Day: {data.frequency[0].day}</Text>
}
</View>
<View style={styles.subContainer1}>
<Text style={styles.startTime}>Start: {data.frequency[0].from}</Text>
<Text style={styles.endTime}>End: {data.frequency[0].to}</Text>
</View>
</View>
)
}
}
Here what i want is that initially i need to display the total data,when click on the text parent class the data need to be changed.I want to show the data in ListView.I already explain above about my problem,So can any one give me suggestions that how to solve my problem, Any help much appreciated
My guess would be that the alert in componentDidMount() is empty because it only called once, at a time when data is still empty, while render() is called on each change of state or props.
I would expect the flow of alerts to look something like this:
Alert called from child's render: "data: (empty)"
Alert called from child's didMount: "didMount:(empty)"
User clicks something in parent, state change, re-render triggered
Alert called from child's render: "data: FOO-content"
React does NOT call componentDidMount() on the second pass. If you want your datasource to be populated on EACH update of the child, you will need to add a componentDidUpdate() method to your child, with copy of the code you have inside componentDidMount().
As a general note: I would advise you to read React's explanation of component lifecycle and methods. I have a feeling that the preparation you are doing inside componentDidMount() is probably better placed elsewhere:
creating a DataSource and initial fill with data from props: in getInitialState()
updating Datasource with data from new props: in componentWillReceiveProps()
I found in React generally that if I want to prepare the initial state of my component based on this.props then I should do it in componentDidUpdate instead of componentDidMount. Components aren't mounted with populated props from the parent component, only updated with them.
I noticed this when doing normal React and doing console.log(this.props) in the render method of my child component. It would log initally as empty, then log again populated with the props. If I called a this.setState in componentDidMount then the population of this.props occurs in the third render. This is because calling this.setState in componentDidMount triggered a pre-update render.
I hope this makes sense and applies to your situation - I arrived at your question looking for a solution to similar behaviour.

Resources