TeodorKolev TeodorKolev - 2 months ago 20
React JSX Question

React transfer data through components

I have parent component and child(listView) component. My target is to send backend dispatched data from parent to child. I achieve that via button click. Problem is that child renders after second parent button click. Maybe my mistake is somewhere in

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

Answer

Alright, a general advice is to always keep a single source of truth:

  • do not copy the data that you already have in props to your internal component state. Use the data in props.

  • try to create your components as stateless as possible (see above...use props, or have the component listen to a 'store'. See Redux or AltJs).


Specifically to try to solve your issue:

In parent replace:

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

with

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

And in ListViewComponent, don't do:

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

but do:

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

The above is untested code, but should serve as a guide to what approach to follow.