2016-05-16 5 views
9

Ich habe eine Liste von Optionen mit Kontrollkästchen und eine Schaltfläche innerhalb eines Elternteils ListView. Wenn die Schaltfläche Fertig gedrückt wird, möchte ich wissen, welche der Kontrollkästchen aktiviert sind.Wie kann ich den Status der untergeordneten Checkbox-Komponenten in einer übergeordneten ListView-Komponente mithilfe von React-Native erhalten?

Ich sollte hinzufügen, dass ich versucht habe, ein Array der Kontrollkästchen in der ListView mit Callback-Funktionen von ChildCheckBox zu pflegen. Es funktionierte gut, außer beim Zurückgehen auf die ListView würde das Array zurückgesetzt werden, während die Kontrollkästchen immer noch aktiviert schienen. Ich würde es vorziehen, die onDonePress() Funktion nur abzufragen, welche Kästchen überprüft werden, dann entsprechend reagieren zu diesem Zeitpunkt, anstatt auf die ListView Wartung eines Arrays angewiesen.

Hier ist die ListView:

class ParentListView extends Component { 
    constructor(props) { 
    super(props); 
    this.state = { 
     dataSource: new ListView.DataSource({ 
     rowHasChanged: (row1, row2) => row1 !== row2, 
     }), 
    }; 
    } 

    componentDidMount() { 
    this.setState({ 
     dataSource: this.state.dataSource.cloneWithRows(ROW_DATA), 
    }); 
    } 

    onCheckPress() { 
    console.log('Check Pressed') 
    // callback from ChildCheckBoxCell...? 
    } 

    onDonePress() { 
    console.log('Done pressed') 
    // callback from ChildDoneCell...? 
    } 

    render() { 
    return (
     <ListView 
     dataSource={this.state.dataSource} 
     renderRow={this.renderRow.bind(this)} 
     style={styles.listView} 
     /> 
    ); 
    } 

    renderRow(cell) { 
    if (cell.type === 'ChildCheckBoxCell') { 
     return (
     <ChildCheckBoxCell onChange={() => this.onCheckPress()} /> 
    ); 
    } 

    if (cell.type === 'ChildDoneCell') { 
     return (
     <ChildDoneCell onDonePress={() => this.onDonePress()}/> 
    ); 
    } 
    } 
} 

Und hier ist die ChildCheckBoxCell Komponente:

class ChildCheckBoxCell extends Component { 

constructor(props) { 
    super(props); 
    this.state = { 
     isChecked: false, 
    }; 
    } 

    onChange() { 
    this.setState({isChecked: !this.state.isChecked}); 
    //Callback... 
    this.props.onChange(); 
    } 

    render() { 
    return (
     <TouchableHighlight onPress={() => this.onChange()}> 
     <Text>{this.state.isChecked? 'Checked' : 'UnChecked'}</Text> 
     </TouchableHighlight> 
    ); 
    } 
} 

Und schließlich ist hier die ChildDoneCell Komponente

class ChildDoneCell extends Component { 

    onDonePress() { 
    //Callback... 
    this.props.onDonePress(); 
    } 

    render() { 
    return (
     <TouchableHighlight onPress={() => this.onDonePress()}> 
     <Text>DONE</Text> 
     </TouchableHighlight> 
    ); 
    } 
} 

Vielen Dank im Voraus!

Antwort

4

Folgendes sollten Sie tun. Ich habe Kommentare in den Code eingefügt, um sie zu erklären. Es sollte 6 Schritte geben.

class ParentListView extends Component { 
    constructor(props) { 
    super(props); 
    this.state = { 
     dataSource: new ListView.DataSource({ 
     rowHasChanged: (row1, row2) => row1 !== row2, 
     }), 
    }; 
    } 

    componentDidMount() { 
    this.setState({ 
     dataSource: this.state.dataSource.cloneWithRows(ROW_DATA), 
    }); 
    } 

    // 1. Change your callback functions to class properties 
    // this way it is auto-bound to this class instance and you don't bind it during render, which 
    // creates rendering overhead. Notice how the selected `cell` is 
    // passed in here. It will be explained in the last steps how that happens. 
    onCheckPress = (cell) => { 
    // Update the `isChecked` state of this cell and update 
    // your `ListView.DataSource` with it 
    console.log('Check Pressed', cell); 
    // callback from ChildCheckBoxCell...? 
    }; 

    // 2. Do the same thing here as step 1. 
    onDonePress = (cell) => { 
    console.log('Done pressed', cell); 
    // callback from ChildDoneCell...? 
    } 

    render() { 
    return (
     <ListView 
     dataSource={this.state.dataSource} 
     renderRow={this.renderRow.bind(this)} 
     style={styles.listView} 
     /> 
    ); 
    } 

    renderRow(cell) { 
    if (cell.type === 'ChildCheckBoxCell') { 
     return (
     // 3. You should pass in the cell data down here AND you should 
     // pass a reference to your callback 
     <ChildCheckBoxCell cell={cell} onChange={this.onCheckPress} /> 
    ); 
    } 

    if (cell.type === 'ChildDoneCell') { 
     // 4. Do the same thing here, except change the reference of 
     // the callback to the other callback, obviously 
     return (
     <ChildDoneCell cell={cell} onDonePress={this.onDonePress}/> 
    ); 
    } 
    } 
} 

class ChildCheckBoxCell extends Component { 

    render() { 
    return (
     // 5. Dereference the function `onChange` and bind it to your 
     // `cell` object, don't worry about `null` changing your 
     // `this` context, it won't. This is how the `cell` object is 
     // passed an argument in the method on step 1. 
     <TouchableHighlight onPress={this.props.onChange.bind(null, this.props.cell)}> 
     {/* Stop using `state` to keep track of `isChecked`, you'll 
      lose this state if this ever is torn down and re-rendered 
      from the parent component */} 
     <Text>{this.props.cell.isChecked? 'Checked' : 'UnChecked'}</Text> 
     </TouchableHighlight> 
    ); 
    } 
} 

class ChildDoneCell extends Component { 

    render() { 
    return (
     // 6. This is the same thing as step 5. 
     <TouchableHighlight onPress={this.props.onDonePress.bind(null, this.props.cell)}> 
     <Text>DONE</Text> 
     </TouchableHighlight> 
    ); 
    } 
} 

Sie werden feststellen, dass Sie die cell Daten in der renderRow Funktion binden könnte, aber es ist nicht bevorzugt. Als Faustregel gilt, dass Sie Ihre Callback-Funktion aus Performancegründen so weit wie möglich auf Ihre untergeordneten Daten dereferenzieren möchten, und weil es aus Wartungsgründen besser ist, explizit zu sein. Dies ist die Abkürzung Alternative:

// ParentListView 
renderRow(cell) { 
    if (cell.type === 'ChildCheckBoxCell') { 
    return (
     // No more passing of cell, but now I'm binding it with the 
     // cell object 
     <ChildCheckBoxCell onChange={this.props.onCheckPress.bind(null, cell)} /> 
    ); 
    } 

    if (cell.type === 'ChildDoneCell') { 
    // Same thing 
    return (
     <ChildDoneCell onDonePress={this.onDonePress.bind(null, cell)}/> 
    ); 
    } 
} 

// ChildCheckBoxCell 

render() { 
    return (
    // Notice how you lose the explicitness of your callback 
    // function and have no idea that this function implicitly passes 
    // the `cell` object because you prematurely bound it with the `cell` object 
    <TouchableHighlight onPress={this.props.onChange}> 
     <Text>{this.props.isChecked? 'Checked' : 'UnChecked'}</Text> 
    </TouchableHighlight> 
); 
} 

EDIT

ich den Code aktualisiert, um mehr Sinn zu machen und unnötiger Instanz Methoden loszuwerden. Ich würde sehr empfehlen, dass Sie die state in Ihrem ChildCheckBoxCell loswerden und versuchen, es über props als Teil Ihrer cell Objekt in meinem ersten Beispiel zu füttern.

+0

Danke für die sehr detaillierte Antwort! Ich habe gerade versucht, es zu implementieren, und aus irgendeinem Grund reagiert die CheckboxCell nicht auf die Änderungen in der ListView. In Schritt 1 habe ich 'cell.isChecked =! Cell.isChecked', aber die Komponente und reagiert nicht auf diese Änderungen. Außerdem wird 'componentWillReceiveProps()' nicht in der 'CheckBoxCell' aufgerufen. –

+0

Wenn Sie die Datenquelle aktualisieren, müssen Sie cloneWithRows verwenden. Kennen Sie das? – rclai

+0

Der fehlende Link ist, dass ich die Datenquelle nicht aktualisiere, ich werde das jetzt versuchen, danke –

Verwandte Themen