首页 文章

React Native:如何从子组件上的事件中重新呈现父组件

提问于
浏览
0

我有两个组成部分 .

Parent ComponentApp.js

Child ComponentLogitem.js

父组件呈现子组件列表 .

每个子组件都有一个文本元素,当单击文本元素时,它会显示一个模态 .

模态有一个删除按钮,它执行删除操作 .

所有这一切都很好 .

当我点击模态内的删除按钮时,我正在设置一个布尔变量来隐藏也适用的模态 .

但是显示的列表(包含子组件数组)不是最新的,即删除的元素仍然出现在列表中 .

有没有办法重新渲染父组件的 render() 方法 .

我尝试通过子组件更新父组件的状态( count ),但仍然没有运气 .

我相信如果更改父组件的状态,将调用父组件的render(),但这不会发生 .

有人可以告诉我这里可以做些什么吗?

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'
  },
};

2 回答

  • 2

    deleteSelectedRecord 移动到父级并在其中更新其状态 setState({ allLogs: [...] }) .

    通过这样做,您可以触发父级重新呈现自己,并且应该再次更新列表 .

    最愚蠢的 Logitem 越好 . 想想你如何编写测试,例如它必须伪造这个删除操作 .

  • 1

    您正尝试在父组件中递增 count 但不会更改 this.state.allLogs ,这是列表的输入 . 当您调用 incrementCounter 时,可能会传递正在删除的项目,以便您可以从提供列表的阵列中删除它 .

    唯一的缺点是你的手上可能有一个数组,它不代表数据库中数组的实际状态 . (数据不一致)

    那么,您可以执行以下操作:从子组件中删除DB中的项目,然后调用 this.props.notifiyParent (重命名为 incrementCounter ),并在父级中定义 notifyParent ,您可以检索 this.state.allLogs 的值并更新父级的状态 - > this将触发重新渲染,您的父组件现在将显示更新的列表 .

    另外,正如@mersocarlin建议的那样,子组件最好是"dumb",因为它不必带有删除项目的逻辑 . 它只需要调用父对象传递的 delete 方法,并在父对象中定义 delete 方法 . 此外,这种方式所有数据库事务都是从一个地方(父项)执行的

相关问题