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

时间:2016-05-16 01:32:11

标签: listview react-native components parent-child state

我有一个选项列表,其中包含复选框和父ListView内的完成按钮。按下完成按钮后,我想知道选中了哪个复选框。

我应该补充一点,我已尝试使用ListView中的回调函数维护ChildCheckBox中的复选框数组。它工作正常,除了导航回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>
    );
  }
}

提前致谢!

1 个答案:

答案 0 :(得分:4)

这是你应该做的。我在代码中包含了注释来解释。应该有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>
    );
  }
}

您会注意到您可以绑定cell函数中的renderRow数据,但不是首选。遵循的经验法则是,出于性能原因,您希望尽可能远地将您的回调函数取消引用到您的子数据,并且因为出于维护原因而更明确。这是替代方案:

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

修改

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