如何使用 React-Native 获取父 ListView 组件中子复选框组件的状态?

How can I get the state of child checkbox components within a parent ListView component using React-Native?

我有一个带有复选框的选项列表和一个父 ListView 中的完成按钮。当按下完成按钮时,我想知道选中了哪些复选框。

我应该补充一点,我尝试使用 ChildCheckBox 中的回调函数在 ListView 中维护一组选中框。它工作正常,除了当导航回 ListView 时,数组将被重置,而复选框似乎仍被选中。我宁愿让 onDonePress() 函数只查询选中了哪些框,然后在那个时候做出相应的响应,而不是依赖 ListView 维护一个数组。

这里是 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()}/>
      );
    }
  }
}

这里是 ChildCheckBoxCell 组件:

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>
    );
  }
}

最后,这里是 ChildDoneCell 组件

class ChildDoneCell extends Component {

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

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

提前致谢!

这是您应该做的。我在代码中加入了注释来解释。应该有6个步骤。

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>
    );
  }
}

您会注意到您可以在 renderRow 函数中绑定 cell 数据,但这不是首选。要遵循的经验法则是,出于性能原因,您希望尽可能低地解除对子数据的回调函数的引用,并且出于维护原因,最好是显式的。这是替代的快捷方式:

// 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>
  );
}

编辑

我更新了代码以使其更有意义并摆脱了不必要的实例方法。在我的第一个示例中,我强烈建议您摆脱 ChildCheckBoxCell 中的 state 并尝试通过 props 将其作为 cell 对象的一部分。