我有以下情况。我有一个父组件,其中包含一个项目列表,其中任何项目都可以在子组件中向下钻取和查看。从子组件中,您应该能够更改正在查看的项目中的值。
在React网络世界中,将父列表作为状态存储,并将项目和回调作为道具传递给子项,这很容易解决。
使用React Native,似乎失去了这种可能性,因为从子组件引起的更改不会触发重新渲染,直到导航为止。
我录制了一段视频。 https://gfycat.com/GreenAgitatedChanticleer
代码如下。
var React = require('react-native');
var {
AppRegistry,
Navigator
} = React;
var List = require('./list');
var listviewtest = React.createClass({
render: function() {
return (
<Navigator
initialRoute={{ component: List }}
renderScene={(route, navigator) => {
return <route.component navigator={navigator} {...route.passProps} />;
}} />
);
}
});
AppRegistry.registerComponent('listviewtest', () => listviewtest);
var React = require('react-native');
var _ = require('lodash');
var {
View,
Text,
TouchableHighlight,
ListView
} = React;
var Detail = require('./detail');
var List = React.createClass({
getInitialState() {
var LANGUAGES = [
{ id: 1, name: 'JavaScript' },
{ id: 2, name: 'Obj-C' },
{ id: 3, name: 'C#' },
{ id: 4, name: 'Swift' },
{ id: 5, name: 'Haskell' }
];
var ds = new ListView.DataSource({ rowHasChanged: (a, b) => a !== b })
return {
languages: LANGUAGES,
ds: ds.cloneWithRows(LANGUAGES)
};
},
goToLanguage(language) {
this.props.navigator.push({
component: Detail,
passProps: {
language: language,
changeName: this.changeName
}
});
},
changeName(id, newName) {
var clone = _.cloneDeep(this.state.languages);
var index = _.findIndex(clone, l => l.id === id);
clone[index].name = newName;
this.setState({
languages: clone,
ds: this.state.ds.cloneWithRows(clone)
});
},
renderRow(language) {
return (
<TouchableHighlight onPress={this.goToLanguage.bind(this, language)}>
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', paddingTop: 5, paddingBottom: 5, backgroundColor: '#fff', marginBottom: 1 }}>
<Text style={{ marginLeft: 5, marginRight: 5 }}>{language.name}</Text>
</View>
</TouchableHighlight>
);
},
render() {
return (
<View style={{ flex: 1, backgroundColor: '#ddd' }}>
<Text style={{ marginTop: 60, marginLeft: 5, marginRight: 5, marginBottom: 10 }}>Select a language</Text>
<ListView
dataSource={this.state.ds}
renderRow={this.renderRow} />
</View>
);
}
});
module.exports = List;
var React = require('react-native');
var {
View,
Text,
TouchableHighlight
} = React;
var Detail = React.createClass({
changeName() {
this.props.changeName(this.props.language.id, 'Language #' + Math.round(Math.random() * 1000).toString());
},
goBack() {
this.props.navigator.pop();
},
render() {
return (
<View style={{ flex: 1, backgroundColor: '#ddd', alignItems: 'center', justifyContent: 'center' }}>
<Text>{this.props.language.name}</Text>
<TouchableHighlight onPress={this.changeName}>
<Text>Click to change name</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.goBack}>
<Text>Click to go back</Text>
</TouchableHighlight>
</View>
);
}
});
module.exports = Detail;
答案 0 :(得分:3)
Turns out this behavior is intentional, at least for now. There's a discussion thread here: https://github.com/facebook/react-native/issues/795
For anyone looking for a workaround, I'm using RCTDeviceEventEmitter to pass data across Navigator. Updated code below
var React = require('react-native');
var _ = require('lodash');
var {
View,
Text,
TouchableHighlight,
ListView
} = React;
var Detail = require('./detail');
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
var List = React.createClass({
getInitialState() {
var LANGUAGES = [
{ id: 1, name: 'JavaScript' },
{ id: 2, name: 'Obj-C' },
{ id: 3, name: 'C#' },
{ id: 4, name: 'Swift' },
{ id: 5, name: 'Haskell' }
];
var ds = new ListView.DataSource({ rowHasChanged: (a, b) => a !== b })
return {
languages: LANGUAGES,
ds: ds.cloneWithRows(LANGUAGES)
};
},
goToLanguage(language) {
this.props.navigator.push({
component: Detail,
passProps: {
initialLanguage: language,
changeName: this.changeName
}
});
},
changeName(id, newName) {
var clone = _.cloneDeep(this.state.languages);
var index = _.findIndex(clone, l => l.id === id);
clone[index].name = newName;
RCTDeviceEventEmitter.emit('languageNameChanged', clone[index]);
this.setState({
languages: clone,
ds: this.state.ds.cloneWithRows(clone)
});
},
renderRow(language) {
return (
<TouchableHighlight onPress={this.goToLanguage.bind(this, language)}>
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', paddingTop: 5, paddingBottom: 5, backgroundColor: '#fff', marginBottom: 1 }}>
<Text style={{ marginLeft: 5, marginRight: 5 }}>{language.name}</Text>
</View>
</TouchableHighlight>
);
},
render() {
return (
<View style={{ flex: 1, backgroundColor: '#ddd' }}>
<Text style={{ marginTop: 60, marginLeft: 5, marginRight: 5, marginBottom: 10 }}>Select a language</Text>
<ListView
dataSource={this.state.ds}
renderRow={this.renderRow} />
</View>
);
}
});
module.exports = List;
var React = require('react-native');
var {
View,
Text,
TouchableHighlight
} = React;
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
var Detail = React.createClass({
getInitialState() {
return {
language: this.props.initialLanguage,
subscribers: []
};
},
componentDidMount() {
var subscriber = RCTDeviceEventEmitter.addListener('languageNameChanged', language => {
this.setState({ language });
});
this.setState({
subscribers: this.state.subscribers.concat([subscriber])
});
},
componentWillUnmount() {
this.state.subscribers.forEach(sub => {
console.log('removing');
sub.remove();
});
},
changeName() {
this.props.changeName(this.state.language.id, 'Language #' + Math.round(Math.random() * 1000).toString());
},
goBack() {
this.props.navigator.pop();
},
render() {
return (
<View style={{ flex: 1, backgroundColor: '#ddd', alignItems: 'center', justifyContent: 'center' }}>
<Text>{this.state.language.name}</Text>
<TouchableHighlight onPress={this.changeName}>
<Text>Click to change name</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.goBack}>
<Text>Click to go back</Text>
</TouchableHighlight>
</View>
);
}
});
module.exports = Detail;
答案 1 :(得分:0)
我也希望将道具改变传播到路线堆栈的其余部分。而且我找不到从第一条路线渲染renderScene()
的任何方法。所以我使用navigator.replace()
而不是更新道具。我正在寻找更好的方法来解决这个问题,因为我相信有很多用例来处理有{info}的route[0]
,需要将更改传播到路由堆栈的其余部分,就像我们在父母和孩子之间的React道具上做的那样。
# this is on parent component and the change is pushed to props(I'm using Redux)
componentWillReceiveProps(nextProps){
this.props.hubs.map((currentHub) => {
nextProps.hubs.map((hub) => {
if(currentHub.updatedAt !== hub.updatedAt){
this.props.navigator.getCurrentRoutes().map((r, index) => {
if(r.getHubId && ( r.getHubId() === hub.objectId ) ){
let route = Router.getHubRoute(hub);
this.props.navigator.replaceAtIndex(route, index);
}
});
}
})
})