【问题标题】:How can I get the state of child checkbox components within a parent ListView component using React-Native?如何使用 React-Native 获取父 ListView 组件中子复选框组件的状态?
【发布时间】:2016-09-11 17:55:24
【问题描述】:

我在父 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>
    );
  }
}

提前致谢!

【问题讨论】:

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


    【解决方案1】:

    这是你应该做的。我在代码中包含了 cmets 来解释。应该有 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 对象的一部分在我的第一个示例中。

    【讨论】:

    • 感谢非常详细的回答!我刚刚尝试实现它,由于某种原因,CheckboxCell 没有响应在 listView 中所做的更改。在第 1 步中,我有 cell.isChecked = !cell.isChecked,但组件并没有响应这些更改。此外,componentWillReceiveProps() 不会在 CheckBoxCell 中被调用。
    • 更新数据源时必须使用 cloneWithRows。你熟悉吗?
    • 缺少的链接是我没有更新数据源,我现在试试,谢谢
    • 成功了,一旦我意识到我需要根据更改重新创建数据源,再次感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-06
    • 1970-01-01
    相关资源
    最近更新 更多