ReactNative 研究

2016-12-15  本文已影响0人  338d708389ae

最近公司发现用 ReactNative 的 ListView 中的 Cell 并不像原生 TableView 中那样重用,一旦有多个Cell 被加载,app 的内存就唰唰得往上飙。
所以需要寻找解决方法,经过一番 Google 发现外国不少人也在表示这个问题
最后发现一个解决方法,其中的想法比较特别,所以我们来研究一下
Recycling Rows for High Performance React Native List Views》。
他的主要思想是:

const ROWS_FOR_RECYCLING = 20;
const bodyComponents = [];
for (let i = 0; i < ROWS_FOR_RECYCLING; i++) {  
    bodyComponents.push(    
        <ReboundRenderer key={'r_' + i} boundTo={this.state.binding[i]} render={this.props.renderRow} />  
    );
}

然后在 Render 的时候把bodyComponents放 <b>原生组件RNTableViewChildren</b> 里面

render() {  
    console.log("重画了!!!!!");  
    return (    
                <View style={{flex: 1}}>      
                    <RNTableViewChildren        
                            style={{flex: 1}}       
                            onChange={this.onBind.bind(this)}        
                            rowHeight={this.props.rowHeight}        
                            numRows={this.props.numRows}>        
                        {bodyComponents}      
                    </RNTableViewChildren>    
                </View>  
    );
}

之后在<b>原生组件RNTableViewChildren</b>的 insertReactSubview 中可以捕捉到嵌套的 View

 - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{
    // will not insert because we don't need to draw them
    //   [super insertSubview:subview atIndex:atIndex]; //不需要做任何处理
    [_unusedCells addObject:subview];
}

这样就能把 javaScript 中创建的 View 加入到

把binding数组放到 State 中,修改 State 的 Binding 刷新视图

const binding = [];
for (let i = 0; i < ROWS_FOR_RECYCLING; i++) {
    binding.push(i);
}
this.state = {  binding: binding // childIndex -> rowID};

原生代码中发送onChange 消息

 - (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
      static NSString *cellIdentifier = @"CustomCell";
  
      TableViewCell *cell = (TableViewCell *)[theTableView dequeueReusableCellWithIdentifier:cellIdentifier];
      if (cell == nil) {
        cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
        cell.cellView = [self getUnusedCell];
        NSLog(@"Allocated childIndex %d for row %d", (int)cell.cellView.tag, (int)indexPath.row);
      } else {
        NSLog(@"Recycled childIndex %d for row %d", (int)cell.cellView.tag, (int)indexPath.row);
      }
  
      NSDictionary *event = @{
                          @"target": cell.cellView.reactTag,
                          @"childIndex": @(cell.cellView.tag),
                          @"rowID": @(indexPath.row),
                          @"sectionID": @(indexPath.section),
                        };
  
      [_eventDispatcher sendInputEventWithName:@"onChange" body:event];
  
      return cell;
}

javaScript端接收到 Onchage 消息,就修改 State 触发刷新

onBind(event) {  
    const {target, childIndex, rowID, sectionID} = event.nativeEvent;  
    bodyComponents[childIndex] = (    
        <ReboundRenderer key={'r_' + childIndex} boundTo={rowID} render={this.props.renderRow} /> );  
    this.state.binding[childIndex] = rowID;  
    this.setState({    
      binding: this.state.binding  
    });  
}

ReboundRenderer通过 React 的刷新机制,重写shouldComponentUpdate,来判断是否需要刷新
从而只刷新需要更新的 View

shouldComponentUpdate: function(nextProps): boolean {  
    return nextProps.boundTo !== this.props.boundTo;
},

整个项目的想法很精妙,利用原生的重用来解决 reactNative 没解决的性能问题,其中reactNative只需要画出 Cell 的 View 即可,其他复杂的要求都被原生代替,原生只作为一个触发器,触发一些特定的刷新视图事件。但由于其基本视图使用的是原生的 TableView,很难和 reactNative 的组件结合,譬如下拉刷新,所以实用性比较低。但这个项目给了我们一个解决一些奇特要求方法,使reactNative很好的和原生结合。个人觉得很值得学习。

参考资料

react native Android 真正回收复用 RecyclerView/ListView

上一篇 下一篇

猜你喜欢

热点阅读