React 通过组件传递数据

React transfer data through components

我有 parent 组件和 child(listView) 组件。我的目标是将后端调度数据从 parent 发送到 child。我通过单击按钮实现了这一点。问题是 child 在第二次 parent 按钮点击后呈现。也许我的错误在 componentWillReceiveProps?

的某处

Parent:

class Parent extends Component {
    constructor(props) {
        super(props);
        this.state = {
            dataSource: '',
            noRepos: false
        };
    }

    componentWillReceiveProps(newProps) {
        this.setState({
            dataSource: newProps.data
        });
        this.validateData(newProps)
    }
    submitUsername() {
        if(this.validateInput()){
            this.props.dispatch({type: 'DUMMY', state: this.state.username});
        } else {
            this.setState({emptyInput: true});
        }
    }
    render() {
        return (
            ...
            <View>
                <ListViewComponent dataRecieved={this.state.dataSource} />
            </View>
            ...
        );
    }
}

export default connect(state => {
    return {
        data: state.data,
    };
})(Parent);

Child:

export default class ListViewComponent extends Component {
    constructor(props) {
        super(props);
        this.state = {
            dataSource: new ListView.DataSource({
                rowHasChanged: (r1, r2) => r1 !== r2,
            }),
        };
    }

    propTypes: {
        dataRecieved: PropTypes.func.string
    };

    componentWillReceiveProps(newProps) {
        this.setState({
            dataSource: this.state.dataSource.cloneWithRows(this.props.dataRecieved),
        });
    }
    renderRow(rowData) {
        return (
            <View>
                <Text>{rowData.name}</Text>
            </View>
        );
    }
    render() {
        return (
            <View>
                <ListView dataSource={this.state.dataSource}
                          enableEmptySections={true}
                          renderRow={this.renderRow} />
            </View>
        );
    }
}

好的,一般建议是始终保持单一的真实来源:

  • 不要将 props 中已有的数据复制到内部组件状态。使用道具中的数据。

  • 尝试创建尽可能无状态的组件(参见上文...使用 props,或者让组件监听 'store'。参见 Redux 或 AltJs)。


专门尝试解决您的问题:

在父级中替换:

  <ListViewComponent dataRecieved={this.state.dataSource} />

  <ListViewComponent dataRecieved={this.props.data} />

而在 ListViewComponent 中,不要这样做:

    this.setState({
        dataSource: this.state.dataSource.cloneWithRows(this.props.dataRecieved),
    });

但是:

render() {

    var ds = new ListView.DataSource({
                    rowHasChanged: (r1, r2) => r1 !== r2,
    })
    , dataSource = ds.cloneWithRows(this.props.dataRecieved);

    return (
        <View>
                <ListView dataSource={dataSource}
            enableEmptySections={true}
            renderRow={this.renderRow} />
        </View>
    );
}

以上是未经测试的代码,但应作为遵循何种方法的指南。

您最初实现 ListViewComponent 的方式很好,您应该在刷新 ListView 时使用 componentWillReceiveProps。那里的每个最佳实践都说要这样做(只是 Google react native redux listview)。我在上面的评论中提到的你的实现中有一个小错误。此外,您 不应该 render 函数中重新创建 ListView.DataSource,这对性能不利并且会破坏 rowHasChangedListView.DataSource.

我说的错误在这里:

componentWillReceiveProps(newProps) {
  this.setState({
    dataSource: this.state.dataSource.cloneWithRows(this.props.dataRecieved),
  });
}

应该是:

// ListViewComponent
componentWillReceiveProps(newProps) {
  this.setState({
    dataSource: this.state.dataSource.cloneWithRows(newProps.dataRecieved),
  });
}

此外,您的 Parent 不应在其 state 中包含 dataSource,只需将 data 直接传递给 ListViewComponent,因为 Redux 正在传递它已经是 prop:

class Parent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      // ...
    };
  }

  componentWillReceiveProps(newProps) {
    // ...
  }

  submitUsername() {
    if (this.validateInput()) {
      this.props.dispatch({ type: 'DUMMY', ... });
    } else {
      // ...
    }
  }
  render() {
    return (
      ...
      <View>
        {/* this.props.data automatically comes from the `connect` wrapper below */}
        <ListViewComponent dataRecieved={this.props.data} />
      </View>
      ...
    );
  }
}

export default connect(state => {
  return {
    data: state.data,
  };
})(Parent);

你也应该看看这个 gist。这是一个如何将 Redux 与 ListView 一起使用的示例。他的示例使用 cloneWithRowsAndSections,但因为您没有节,所以您只需改用 cloneWithRows 即可。这个要点是由一位非常活跃的核心 React Native 开发人员编写的。