ListView.DataSource#cloneWithRows 的参数问题



  • 如果 cloneWithRows 的参数如果是复杂类型数组,自己做了一下试验,必须clone一下换成新的数组之后,setState才会触发UI update,高手帮解释一下问什么?

    var _ds = JSON.parse(JSON.stringify(f)); // clone datasorce, force renderRow update
    that.setState({
      dataSource: that.state.dataSource.cloneWithRows(_ds),
      loaded: true
    });
    

    以上代码摘自这个例子里,https://gist.github.com/iahu/0e524f4612a8925f2f9c

    /**
     * Sample React Native App
     * https://github.com/facebook/react-native
     */
    'use strict';
    
    var React = require('react-native');
    var fs = require('NativeModules').FileSystem;
    var {
      AppRegistry,
      StyleSheet,
      Text,
      TouchableHighlight,
      ListView,
      View,
      AlertIOS
    } = React;
    var README_URL = 'https://coding.net/u/levi/p/imouto-host/git/raw/master/README.md';
    var MAP = {'Acrylated':'AcrylicHosts','Hosts-ä': 'Hosts-a'};
    var BASE_URL = 'https://coding.net/u/levi/p/imouto-host/git/raw/master/';
    var GOOGLE_HOSTS_URL = 'https://raw.githubusercontent.com/txthinking/google-hosts/master/hosts';
    
    var aHostsApp = React.createClass({
    	getInitialState: function () {
    		var ds = new ListView.DataSource({
    				rowHasChanged:(r1,r2) => r1 !== r2
    			});
    		return {
    			dataSource: ds.cloneWithRows(['row 1', 'row 2']),
    			pressTest: 0,
    			loaded: false,
    			updatePressed: false
    		};
    	},
    	getData: function () {
    		var that = this;
    		fetch(README_URL)
    			.then((response) => response.text())
    			.then(function (res) {
    				var f = res.match(/\- (.+)/g);
    
    				f = f.map(function(line, idx) {
    					var l = line.replace(/^\s?-\s?/, '') + '\n';
    					var a = l.split(/\s?\:\s?/); 
    					return {
    						title: a.shift(),
    						desc: a.join(':').split(' ').shift()
    					};
    				});
    				f.push({
    					title: 'google-hosts',
    					desc: '每天2:00-3:00'
    				});
    				var _ds = JSON.parse(JSON.stringify(f)); // clone datasorce, force renderRow update
    				
    				that.setState({
    					dataSource: that.state.dataSource.cloneWithRows(_ds),
    					loaded: true
    				});
    			})
    			.done();
    	},
    	componentDidMount: function () {
    		this.getData();
    	},
    	render: function() {
    		if (!this.state.loaded) {
    			return this.renderLoadingView();
    		}
    		return this.renderList();
    	},
    	renderLoadingView: function() {
    	    return (
    	      <View style={styles.loading}>
    	        <Text>
    	          Loading ...
    	        </Text>
    	      </View>
    	    );
    	},
    	renderList: function () {
    		return (
    			<View style={styles.container}>
    				<Text style={styles.title}>aHostsApp{this.state.pressTest.toString()}</Text>
    				<View style={styles.separator} />
    				<ListView
    					style={styles.listGroup}
    					dataSource={this.state.dataSource}
    					renderRow= {this.renderRow} />
    			</View>
    		);
    	},
    	renderRow: function (rowData, sectionID, rowID) {
    		return (
    			<View style={styles.listItem} id={rowID}>
    				<View style={styles.itemContent}>
    				<Text style={styles.itemTitle}>{rowData.title}</Text>
    				<Text style={styles.itemDesc}>更新时间:{rowData.desc}</Text>
    				</View>
    				<TouchableHighlight style={[styles.buttonSmall, this.state.pressTest == 2 && styles.updatePressed]} onPress={()=> this.onItemPress(rowData)} underlayColor="#06f">
    					<Text style={[styles.buttonSmallText, this.state.pressTest == 2 && styles.lightGrayText]}>{this.state.pressTest.toString()}更新</Text>
    				</TouchableHighlight>
    			</View>
    		);
    	},
    	onItemPress: function (rowData) {
    		var that = this;
    		var title = rowData.title;
    		var url;
    
    		title = MAP[title] || title;
    		if ( this.state.updatePressed ) {return;}
    		
    		if (title === 'google-hosts') {
    			url = GOOGLE_HOSTS_URL;
    		} else {
    			url = encodeURI(BASE_URL + title + '/hosts');
    		}
    		// that.setState({updatePressed: true});
    		that.setState({pressTest: 2});
    		console.log('pressTest::::2' ,this.state.pressTest.toString() ); // get 0
    		setTimeout(function(){
    			console.log( that.state.pressTest ) // get 2
    		}, 10);
    		// fetch host
    		fetch(url)
    			.then((res) => res.text())
    			.then(function (res) {
    				fs.writeFile('/etc/hosts', res, function (res) {
    					that.setState({pressTest: 0});
    					// that.setState({updatePressed: false});
    					console.log('pressTest::::0' ,that.state.pressTest.toString() ); // get 2
    					if (res) {
    						console.log('success');
    						// AlertIOS.alert('alert','success');
    					} else {
    						// AlertIOS.alert('alert','failed');
    						console.log('failed');
    					}
    				});
    			});
    	}
    });
    
    var styles = StyleSheet.create({
      container: {
        flex: 1,
        // justifyContent: 'center',
        // alignItems: 'center',
        backgroundColor: '#F5F5F5',
      },
      loading: {
      	height: 36,
      	flex: 1,
      	alignItems: 'center',
      	alignSelf: 'stretch',
      	justifyContent: 'center'
      },
      title: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
        marginTop: 20
      },
      lightGrayText: {
      	color: '#666'
      },
      buttonSmall: {
      	borderRadius: 4,
        alignSelf: 'center',
        alignItems: 'center',
        justifyContent: 'center',
        borderWidth: 1,
        borderColor: '#3385ff',
        paddingTop: 4,
        paddingBottom: 4,
        paddingLeft: 9,
        paddingRight: 9,
        marginRight: 6
      },
      buttonSmallText: {
      	fontSize: 12,
        color: '#3385ff'
      },
      listTitle: {
      	paddingLeft: 6,
      	marginBottom: 10
      },
      listGroup: {
      },
      listItem: {
      	flex: 1,
      	flexDirection: 'row',
      	paddingLeft: 6,
      	paddingRight: 6,
      	paddingTop: 10,
      	paddingBottom: 10,
      	borderWidth: 1,
      	borderColor: '#ddd',
      	marginTop: -1,
      	backgroundColor: '#fff'
      },
      itemContent: {
      	flex: 1,
      	paddingLeft: 10
      },
      itemTitle: {
      	fontSize: 16,
      	marginBottom: 6
      },
      itemDesc: {
      	fontSize: 12,
      	color: '#333'
      },
      separator: {
      	height: 1,
      	backgroundColor: '#eee'
      },
      updatePressed: {
      	backgroundColor: '#eee',
      	borderColor: '#eee'
      }
    });
    
    AppRegistry.registerComponent('aHostsApp', () => aHostsApp);
    


  • 比起简单粗暴地解释,我建议你读一下cloneWithRows方法的源码,然后再思考这个问题