[英]React Native : How to rerender the parent component from an event on the child component
I have two components. 我有两个组成部分。
Parent Component : App.js
父组件 :
App.js
Child Component : Logitem.js
子组件 :
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 ie the deleted element still appears in the list. 但是显示的列表(包含子组件的数组)不是最新的,即删除的元素仍然出现在列表中。
Are there any ways to rerender the render()
method of the parent component. 有什么方法可以重新渲染父组件的
render()
方法。
I have tried updating state of the parent component ( count
) via the child component but still no luck. 我尝试通过子组件更新父组件(
count
)的状态,但还是没有运气。
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. 我相信,如果更改父组件的状态,则将调用父组件的render(),但这不会发生。
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'
},
};
Move deleteSelectedRecord
to the parent and update its state setState({ allLogs: [...] })
in there. 将
deleteSelectedRecord
移到父级,然后在其中更新其状态setState({ allLogs: [...] })
。
By doing that you trigger parent to re-render itself and the list should be updated again. 这样,您可以触发父级重新渲染自身,并且列表应再次更新。
The dumbest Logitem
is, the better. 最笨的
Logitem
更好。 Think of how would you write test for it having to fake this delete action for example. 考虑一下您将如何为必须伪造此删除操作的测试编写测试。
You're trying to increment count
in the parent component but are not altering this.state.allLogs
, which is what feeds the list. 您正在尝试增加父组件中的
count
,但未更改this.state.allLogs
,这正是列表的内容。 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. 当您调用
incrementCounter
,可以向上传递要删除的项目,以便可以将其从提供列表的数组中删除。
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. 因此,您可以执行以下操作:从子组件中删除数据库中的项目,然后调用
this.props.notifiyParent
(重命名为incrementCounter
this.props.notifiyParent
),在定义了notifyParent
的父项中,您可以检索this.state.allLogs
的值并更新父级的状态->这将触发重新渲染,并且父级组件现在将显示更新后的列表。
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. 另外,正如@mersocarlin所建议的那样,子组件最好是“哑巴”的,因为它不必带有删除项目的逻辑。 It just needs to call the
delete
method that the parent would pass down and the delete
method would be defined in the parent. 它只需要调用父级将传递的
delete
方法,并且delete
方法将在父级中定义。 Also, this way all DB transactions are carried out from a single place (the parent) 同样,通过这种方式,所有数据库事务都在一个位置(父级)进行
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.