参考文章
理解
同样类比于我们iOS
的TableView
,我们初始化一个TableView
,需要对下面这几个代理做处理
- cellForRowAtIndexPath —— 制定row的内容
- heightForRow —— 高度
- didselectRowAtIndexPath —— 选中cell的回调
- didDeselectRowAtIndexPath —— 选中后释放选中的cell的回调
相比于上面的四个delegate
,RN
中用了一个renderItem
来囊括。
renderItem
renderItem={({item}) => this._renderItem(item)}
- 这个
props
相当于是每一个cell
的描述,而数据源,就是我们从data
这个props
中穿过去的JSON数组中的单个元素. -
renderItem
中要传一个返回值是组件的函数。
从我们底下这段代码的截图可以看出,renderItem
对应的参数{item}
就是JSON数组的单个元素,因此在我们调用上面的_renderItem(item)
中,可以利用他的index属性对数据进行交互。
- select与否,则采用
JS
对应的state
状态机来控制。这里的思路就是,组件中设定一个state
用来保存选中项目的索引值。当按下某个cell(renderItem)
时,借用他的onPress
回调方法,去更新这个state
从而达到更新背景的效果.
下面是这个组件的参考代码
class PaywayOptions extends Component {
constructor(props) {
super(props);
this.state = {selectedIndex: -1};
}
_renderItem(item) {
let whetherSel = (this.state.selectedIndex === item.index);//
return <TouchableHighlight style={[styles.PaywayContainer, {backgroundColor: whetherSel ? 'blue' : 'white'}]}
onPress={() => {
if (whetherSel === false) {
console.log('选中' + item.index);
this.setState({selectedIndex: item.index})
}
}}>
<Text style={{width: 50, height: 18, color: 'black'}}>{item.key}</Text>
</TouchableHighlight>
};
_seperator(){
return <View style={{backgroundColor:'black',height:1,width:width-60}}/>
}
render() {
return (
<View style={{height: 126, width: width - 40, backgroundColor: 'yellow', alignSelf: 'center'}}>
<FlatList data={[{key: 'a', index: 0}, {key: 'b', index: 1}]}
renderItem={({item}) => this._renderItem(item)}
keyExtractor={(item) => item.index}
ItemSeparatorComponent = {this._seperator.bind(this)}
/>
</View>
)
}
}
Data 赋值问题讨论。
先来看下面两张图,是data
赋值不同时造成的。
下面是代码片段
// ...
fetchData() {
return [{image: unionpayIco, width: 30, height: 20, title: '银联支付', index: 0},
{image: wechatpayIco, width: 26, height: 24, title: '微信支付', index: 1},
{image: alipayIco, width: 25, height: 24, title: '支付宝支付', index: 2},
];
}
_data = [{image: unionpayIco, width: 30, height: 20, title: '银联支付', index: 0},
{image: wechatpayIco, width: 26, height: 24, title: '微信支付', index: 1},
{image: alipayIco, width: 25, height: 24, title: '支付宝支付', index: 2},
];
_renderItem(item) {
let whetherSel = (this.state.selectedIndex === item.index);//
return (
<TouchableOpacity style={[styles.optionContainer, {backgroundColor: whetherSel ? '#EAF3FF' : '#FDFFFD'}]}
activeOpacity={0.5}
onPress={() => {
console.log('当前idx = ' + item.index);
console.log('当前state idx = ' + this.state.selectedIndex);
if (whetherSel === false) {
console.log('是否设置');
this.setState({selectedIndex: item.index})
}
}}>
<View style={styles.imageContainer}>
<Image style={{width: item.width, height: item.height}} source={item.image}/>
</View>
<Text style={styles.text}>{item.title}</Text>
<Image style={{width: 20, height: 20, marginRight: 10}} source={whetherSel ? markedIco : unmarkedIco}/>
</TouchableOpacity>
)
}
render() {
return (
<View style={{height: 126, width: width - 40, alignSelf: 'center', marginTop:this.props.topDistance}}>
<FlatList data= {this._data}//{this.fetchData()}
renderItem={({item}) => this._renderItem(item)}
keyExtractor={(item) => item.index}
scrollEnabled={false}
/>
</View>
);
}
如果使用 fetchData()
,这个返回JSON数组的函数,传值给FlatList
,结果是没有问题的。
但是如果用一个变量去存储data
就会出现右边那张图显示的样子。
出现这种情况的原因是,作为数据变量的 data
不可以就这样写在这个位置。正确的写法,是在构造函数constructor
中去指定一个变量 this.data = []
才可以(不要使用这种错误的写法!)
———— 2018.3.13补充 ——————
概述
经过一阵子对 FlatList
的使用,总结了以下几个小点.
FlatList 不要尝试去实现局部刷新
这点理解起来其实有点费解。
-
设想情况
举一个实际的例子,假设我有一个Flatlist
,他的每一个cell
都很复杂,有些情况下,从需求来看我可能需要去刷新cell
中的某个子控件。
那么我认为我的 cell 里头需要有一个 state 来控制cell 的刷新。但是状态的变化是由数据决定的,换言之,我什么时候需要刷新我的子控件,依然是需要父组件来告诉我的。
那么好了,我们如果写成下面这段设想中的伪代码:// 父控件 _renderItem = (item)=> { <Cell ref={refCmp => this._ref} /> } //... 调用刷新 this.ref.updateCurrentCell(); // 子控件 class Cell extends PureComponent { constructor(props) { super(props); this.state = { needToUpdate:false } updateCurrentCell = ()=> { this.setState({needToUpdate:true}); } } }
这样写的结果是,我不知道我现在选的究竟是哪个 cell,因为每个 renderItem 不像 iOS 中的
didSelectRowAtIndexPath
一样自动的定位到我选的那个CELL
另外,即使这样做可行,当
flatlist
上下滑动,从隐藏到重新 reuse 的情况,RN 不知道你这时候cell
的state
究竟是什么了
所以,要使用下面的情况 -
实际情况
我们必须要将dataSource
或控制子控件刷新的state
写在父组件内。不要害怕说“我明明只需要局部刷新,为什么你要让整个父组件都去跑一边”?需要声明的是,RN 中有一个机制是,渲染的时候会对组件进行比对,若没有做任何改变的组件,是不会执行到其中的
render
方法的。这样就定下了一个
规则,flatList
中其实不存在需要局部刷新。所有的控制,都需要在和flatlist
同级的组件上做state
的控制
DataSource 不要像上面那样写
作为数据,dataSource
建议是写在state
里头,方便管理。原因其实也包括上面的这条了。
另外就是,如果数据源有改变,不要做下面这样的代码写法, 因为这样写,是不会触发render 刷新的!
let tempArray = this.state.dataSource;
tempArray.push({content:'xxx'});
this.setState({dataSource:tempArray})
因为数组的赋值,如果直接使用简单的等号,只会讲指针传过来。换言之,这是一个浅拷贝操作。
正确的写法是:
let tempArray = [...this.state.dataSource];
tempArray.push({content:'xxx'});
this.setState({dataSource:tempArray})