Reputation: 463
I have two components.
Parent Component : App.js
Child Component : Logitem.js
Parent Component renders a list of Child Components.
Each child component has a text element and when the text element is clicked it displays a modal.
The modal has a delete button and it performs a delete operation.
All of this is working fine.
When I click on the delete button inside the modal I am setting a boolean variable to hide the modal which also works.
But the list shown (containing the array of child components) are not the latest i.e the deleted element still appears in the list.
Are there any ways to rerender the render()
method of the parent component.
I have tried updating state of the parent component (count
) via the child component but still no luck.
I believed that if the state of the parent component is changed the render() of the parent component will be called but this is not happening.
Can someone let me know as to what can be done here ?
Parent Component
import React, { Component } from 'react';
import { StyleSheet, View, Text, ScrollView, Modal, DatePickerIOS } from 'react-native';
import {
dropLogsTable,
createLogsTable,
getProfileHeightStandardfromDB,
saveLogsRecord,
populateDummyLogs,
getLogsRecords,
getLogsRecordsFromDB,
neverendingmethod,
getLogsDetailsforSaveDelete
} from '../src/helper';
import { Spinner } from '../src/Spinner';
import Logitem from '../src/Logitem';
export default class App extends Component {
state = {
allLogs:{
rows:{
_array:[{logstringdate:''}]
}
},
profileobject: {profileheight: 100, profilestandard: "XYZ"},
showspinner: true,
count:0
};
componentDidMount() {
this.fetchProfileData();
this.getAllLogs();
}
renderSpinner() {
if(this.state.showspinner) {
return <Spinner size="small" />;
}
else {
//return this.state.allLogs.rows._array.map(ae => <Text>{ae.bmi}</Text>);
return this.state.allLogs.rows._array.map(
(ae) => (
<View
key={ae.logdate}
>
<Logitem
logstringdate={ae.logstringdate}
bmi={ae.bmi}
weight={ae.metricweight}
logdate={ae.logdate}
incrementCount={() => this.setState({count: count+1)}
/>
</View>
)
);
}
}
async fetchProfileData() {
console.log('Before Profile Fetch');
const result = await getProfileHeightStandardfromDB();
console.log('After Profile Fetch');
console.log('Height : '+result.profileheight);
console.log('Standard: '+result.profilestandard);
this.setState({profileobject:result}); //Line Y
return result; //Line X
}
async getAllLogs() {
console.log('Before All Logs Fetch');
const allLogs = await getLogsRecordsFromDB();
console.log('After All Logs Fetch');
console.log('Spinner State ==>'+this.state.showspinner);
if(allLogs != null)
{
this.setState({allLogs, showspinner: false});
console.log('After async spinner state ==>'+this.state.showspinner);
console.log(allLogs);
}
return allLogs;
}
render() {
return (
<ScrollView style={styles.container}>
{this.renderSpinner()}
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
top: {
width: '100%',
flex: 1,
},
bottom: {
flex: 1,
alignItems: 'center',
},
});
Child Component
import React, { Component } from 'react';
import { Text, View, Modal, DatePickerIOS, TextInput, Button } from 'react-native';
import {
deleteSelectedRecordDB
} from '../src/helper';
import { Spinner } from '../src/Spinner';
export default class Logitem extends Component {
constructor(props) {
super(props);
const { logstringdate, bmi, weight, logdate } = this.props;
}
state = {
selecteddate: '1',
selectedweight: this.props.weight,
showmodal: false,
date: new Date(86400000 * this.props.logdate),
}
async deleteSelectedRecord(){
console.log('Delete clicked');
console.log('this.state.selecteddate ==>' + this.state.selecteddate); //LINE X
const result = await deleteSelectedRecordDB(this.props.logdate);
console.log('deleteSelectedRecord after');
console.log('result ==> '+ result);
if (result)
{
this.setState({ showmodal: false });
this.props.incrementCount();
}
return result;
}
setModalVisible = (visible) => {
this.setState({showmodal: visible});
}
onWeightClick = () => {
this.setState({ selecteddate: this.props.logdate, showmodal: true }, () => {
console.log('Value in props==>' + this.props.logdate);
console.log('The selecteddate in the state ==> ' + this.state.selecteddate);
});
}
onDateChange(date) {
this.setState({
date: date
});
}
render() {
return (
<View style={styles.containerStyle}>
<Modal
animationType="slide"
transparent={false}
visible={this.state.showmodal}
onRequestClose={() => {alert("Modal has been closed.")}}
>
<View style={{marginTop: 22}}>
<DatePickerIOS
date={this.state.date}
mode="date"
onDateChange={(date) => this.onDateChange(date)}
style={{ height: 100, width: 300 }}
/>
</View>
<View style={{ marginTop: 22, borderColor: '#ddd', borderWidth: 5 }}>
<TextInput
returnKeyType="done"
keyboardType='numeric'
style={{
height: 40,
width: 60,
borderColor: 'gray',
borderWidth: 1,
}}
onChangeText={(text) => this.setState({ selectedweight: text })}
value={this.state.selectedweight.toString()}
/>
<Text>KG</Text>
<Button
title="Delete"
onPress={this.deleteSelectedRecord.bind(this)}
style={{ marginTop: 200 }}
/>
</View>
</Modal>
<View style={styles.headerContentStyle}>
<Text>{this.props.logstringdate}</Text>
<Text>{this.props.bmi}</Text>
</View>
<View style={styles.thumbnailContainerStyle}>
<Text onPress={this.onWeightClick}>{this.props.weight}</Text>
</View>
</View>
);
}
};
const styles = {
containerStyle: {
borderWidth: 1,
borderRadius: 2,
borderColor: '#ddd',
borderBottomWidth: 0,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2},
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 1,
marginLeft: 5,
marginRight: 5,
marginTop:10,
},
thumbnailContainerStyle: {
justifyContent: 'center',
alignItems: 'center',
marginLeft: 10,
marginRight: 10,
flexDirection: 'row'
},
headerContentStyle: {
flexDirection: 'column',
justifyContent: 'space-around'
},
};
Upvotes: 0
Views: 1809
Reputation: 1093
You're trying to increment count
in the parent component but are not altering this.state.allLogs
, which is what feeds the list. As you call incrementCounter
, maybe pass the item that is being deleted upwards so that you can remove it from the array that feeds the list.
The only down side to this is that you might have an array on your hands that does not represent the actual state of the array in the DB. (data inconsistency)
So, then you could do the following: delete the item from the DB from the child component and then call this.props.notifiyParent
(renamed incrementCounter
) and in the parent where notifyParent
is defined you can retrieve the value for this.state.allLogs
and update the parent's state -> this will trigger a re-render and your parent component will now show the updated list.
Also, as @mersocarlin suggests it's better for the child component to be "dumb" in that it does not have to carry the logic of how the item is deleted. It just needs to call the delete
method that the parent would pass down and the delete
method would be defined in the parent. Also, this way all DB transactions are carried out from a single place (the parent)
Upvotes: 1
Reputation: 7424
Move deleteSelectedRecord
to the parent and update its state setState({ allLogs: [...] })
in there.
By doing that you trigger parent to re-render itself and the list should be updated again.
The dumbest Logitem
is, the better. Think of how would you write test for it having to fake this delete action for example.
Upvotes: 2