简体   繁体   English

React Native:如何从子组件上的事件重新渲染父组件

[英]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.

相关问题 当子组件中发生onclick事件时,如何重新呈现父组件? - how to rerender parent component when onclick event happend in child component? 如何在本机反应中将道具从父组件传递到子组件? - How to pass props from parent component to child component in react native? 如何从 React Native 中的子组件更新父组件中的 State? - How To Update State In Parent Component From Child Component In React Native? 子组件不会重新渲染,但父组件会重新渲染。 如何让子组件重新渲染? - Child component doesn't rerender but parent component does rerender. How to make child component rerender? 在React中重新渲染父组件 - Rerender Parent Component in React 从父级到子级组件响应本机不透明度 - React Native Opacity from Parent to Child Component 使用StackNavigator从子组件到父组件反应本机 - React Native from child component to parent with StackNavigator 如何在React Native中从子组件更改父状态? - How to change parent's state from child component in react native? React Native中如何从子组件调用父函数并将子组件的参数传递给父组件? - How can calling parent function from child component and passing parameters from child component to parent component in React Native? 从子组件触发函数并在React Native中在父组件中定义 - Trigger a function from child component and define in parent component in React Native
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM