npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@qlik/react-native-simple-grid

v1.4.1

Published

A react native straight table component designed for performance

Readme

@qlik-trial/react-native-simple-grid

A straight table component designed for performance

Installation

npm install @qlik-trial/react-native-simple-grid

Architecture

This component is built using React Native's new architecture (Fabric) for optimal performance on both iOS and Android. Below is an overview of how data flows from JavaScript to native rendering.

iOS Architecture Flow

JavaScript (React)
    ↓
TypeScript Codegen Spec (SimpleGridNativeComponent.ts)
    ↓
React Native Codegen → C++ Props Structs
    ↓
Objective-C++ Bridge (ReactNativeStraightTableComponentView.mm)
    ↓
Swift View Layer (ContainerView.swift)
    ↓
Native Rendering (UIKit)

Android Architecture Flow

JavaScript (React)
    ↓
TypeScript Codegen Spec (SimpleGridNativeComponent.ts)
    ↓
React Native Codegen → C++ Props/Events
    ↓
ViewManager with @ReactProp (ReactNativeStraightTableViewManager.java)
    ↓
TableView (Java)
    ↓
Native Rendering (Android Views/RecyclerView)

iOS Detailed Flow

1. JavaScript → Native (Codegen Bridge)

  • Props are defined as TypeScript interfaces in src/specs/SimpleGridNativeComponent.ts
  • React Native Codegen generates C++ structs from these interfaces at build time
  • When props change in React, they are serialized and sent across the bridge as C++ structs

2. Objective-C++ Layer (ios/ReactNativeStraightTableComponentView.mm)

  • The updateProps() method is called automatically by Fabric whenever props change
  • Receives C++ props: const ReactNativeStraightTableViewProps &newViewProps
  • Converts C++ structs to NSDictionary (Objective-C compatible format):
    NSMutableDictionary *theme = [NSMutableDictionary new];
    theme[@"headerBackgroundColor"] = RCTNSStringFromString(newViewProps.theme.headerBackgroundColor);
    _view.theme = theme;
  • Assigns dictionaries to the Swift view instance

3. Swift Layer (ios/ContainerView.swift)

  • Class marked with @objcMembers to expose all properties to Objective-C++:
    @objc
    @objcMembers
    public class ContainerView: UIView {
      public var theme: NSDictionary = [:] {
        didSet {
          // Deserialize NSDictionary → Swift struct
          let json = try JSONSerialization.data(withJSONObject: theme)
          let decoded = try JSONDecoder().decode(TableTheme.self, from: json)
          // Pass to rendering logic
        }
      }
    }
  • @objcMembers on the class makes individual @objc annotations redundant
  • Uses JSONSerialization + JSONDecoder to convert NSDictionary → type-safe Swift structs
  • Property observers (didSet) are automatically triggered when values are set from Objective-C++
  • Decoded structs are passed to rendering components

4. Rendering

  • Swift files (TableViewFactory.swift, DataCollectionView.swift, etc.) use the decoded structs to render the table with proper styling and data

Why This Architecture?

  • @objc Boundary: Swift and Objective-C++ can only share Objective-C-compatible types (NSDictionary, NSString, NSNumber)
  • Type Safety: JSON decoding provides type-safe Swift structs instead of manually extracting dictionary values
  • Fabric Requirement: The Objective-C++ bridge layer is required by React Native's new architecture
  • Performance: Fabric's synchronous rendering eliminates the old bridge's asynchronous overhead

Bridging Between Layers

Swift to Objective-C++ Connection:

#import "react_native_simple_grid-Swift.h"  // Auto-generated bridging header

@implementation ReactNativeStraightTableComponentView {
    ContainerView *_view;  // Swift class accessible from Objective-C++
}

- (instancetype)initWithFrame:(CGRect)frame {
    _view = [[ContainerView alloc] initWithFrame:frame];  // Creates Swift instance
    // ...
}

The bridging header (react_native_simple_grid-Swift.h) is auto-generated by Xcode and exposes all Swift classes marked with @objc or public to Objective-C++.

Android Detailed Flow

Cross-Platform:

  • TypeScript Spec: src/specs/SimpleGridNativeComponent.ts - Defines the props interface for codegen (both platforms)
  • React Component: src/components/SimpleGrid.tsx - Unified component for iOS and Android

Android:

  • ViewManager: android/src/main/java/.../ReactNativeStraightTableViewManager.java - Handles props with @ReactProp
  • TableView: android/src/main/java/.../TableView.java - Main rendering component with RecyclerView
  • Data Layer: android/src/main/java/.../DataProvider.java, RowFactory.java, DataColumn.java

2. ViewManager Layer (android/src/main/java/.../ReactNativeStraightTableViewManager.java)

  • Uses traditional @ReactProp annotations - compatible with both Paper and Fabric
  • Each prop method is called individually when that prop changes:
    @ReactProp(name = "rows")
    public void setRows(View view, @Nullable ReadableMap source) {
      // Process rows prop
    }
  • Handles rowsJSON string format (matching iOS Codegen pattern):
    // Parse JSON string to ReadableArray
    org.json.JSONArray jsonArray = new org.json.JSONArray(rowsJSON);
    // Convert to WritableArray for processing

3. TableView Layer (android/src/main/java/.../TableView.java)

  • Main rendering component using RecyclerView for performance
  • DataProvider manages columns and rows
  • Implements wipe-on-column-change strategy (same as iOS):
    if(tableView.dataProvider.dataColumns.size() != dataColumns.size()) {
      tableView.resetTable();  // Wipe state on column count change
    }

4. Rendering

  • Uses Android's RecyclerView with custom ViewHolder pattern
  • Cell types: text, images, mini-charts, URLs
  • Frozen first column implemented with separate RecyclerView

Android-Specific Implementation Details

JSON String Handling: The rowsJSON prop (a JSON-serialized array) requires manual conversion:

private boolean processRows(TableView tableView, ReadableMap rows) {
  if (rows.hasKey("rowsJSON")) {
    String rowsJSON = rows.getString("rowsJSON");
    org.json.JSONArray jsonArray = new org.json.JSONArray(rowsJSON);
    
    // Manual conversion: JSONArray → WritableArray
    WritableArray writableArray = Arguments.createArray();
    for (int i = 0; i < jsonArray.length(); i++) {
      JSONObject jsonObject = jsonArray.getJSONObject(i);
      WritableMap writableMap = convertJsonToMap(jsonObject);
      writableArray.pushMap(writableMap);
    }
    // Pass to RowFactory for processing
  }
}

Prop Ordering & Initialization: Props arrive individually and asynchronously. The table only initializes when all required props are present:

@ReactProp(name = "rows")
public void setRows(View view, ReadableMap source) {
  tableView.setRows(...);
  // Only initialize if columns, rows, and styles are all present
  if(tableView.isInitialized() && 
     tableView.cellContentStyle != null && 
     tableView.headerContentStyle != null) {
    tableView.initialize();
  }
}

Race Condition Prevention: When switching between tables (different column configurations):

@ReactProp(name = "cols")
public void setCols(View view, ReadableMap source) {
  List<DataColumn> dataColumns = processColumns(tableView, source);
  if(tableView.dataProvider.dataColumns != null) {
    if(tableView.dataProvider.dataColumns.size() != dataColumns.size()) {
      tableView.resetTable();  // Wipe everything on column count change
      tableView.setDataColumns(dataColumns);
    } else {
      tableView.setDataColumns(dataColumns);
    }
  }
}

Platform Differences Summary

| Aspect | iOS | Android | |--------|-----|---------| | Bridge Pattern | Obj-C++ → NSDictionary → Swift | @ReactProp → Direct Java | | Type Safety | JSONDecoder to Swift structs | ReadableMap to Java objects | | Prop Updates | Batch via didSet | Individual method calls | | Row Data Format | rowsJSON string parsed in Swift | rowsJSON string parsed in Java | | Initialization | Guards in didSet | Guards in @ReactProp + isInitialized() check | | Race Prevention | dataColumns = nil in Swift | resetTable() in Java | | Rendering | UICollectionView | RecyclerView |

Both platforms now share the same TypeScript codegen spec and implement the same wipe-on-column-change strategy for race condition prevention.

Critical Implementation Patterns

Fabric Prop Update Behavior

In React Native's new architecture (Fabric), props update immediately and individually rather than being batched. Each prop's didSet handler fires as soon as the prop is received, which can create race conditions if not handled carefully.

Race Condition Prevention: Wipe-on-Column-Change

When switching between different tables (different column configurations), a critical race condition can occur:

  1. cols prop updates with new table's columns → cols didSet fires
  2. Old dataRows still present from previous table
  3. Attempting to render with mismatched columns/rows → crash

Solution: Wipe all state when column count changes:

// In ContainerView.swift
if dataColumns != nil && decodedCols.header?.count != dataColumns?.count {
  // Column count changed - wipe everything
  dataColumns = nil
  dataRows = nil
  totals = nil
  // Store new columns but don't render yet
  dataColumns = decodedCols.header
  return  // Wait for fresh rows
}

This pattern ensures the table only renders when it has a complete, consistent data set.

React Component Identity

Use a stable key prop based on table identity to ensure proper React lifecycle:

<SimpleGrid
  key={layout?.qInfo?.qId || 'default'}
  // ...other props
/>

This forces React to remount the component when switching between different tables, complementing the native wipe strategy.

Selection Clearing Behavior

The clearSelections prop gates when selections are cleared:

// Only clear when explicitly requested
if decodedRows.reset == true && 
   clearSelections != nil && 
   clearSelections!.compare("yes") == .orderedSame {
  selectionsEngine.clear()
}

This allows selections to persist during normal data updates while still supporting explicit clearing when clearSelections="yes" is set.

Event Serialization

Complex objects in events are serialized as JSON strings due to Codegen limitations:

// Event structure
onSearchColumn?: (event: {
  target: Double;
  column: string;  // JSON serialized DataColumn
}) => void;

Wrapper functions in SimpleGrid.ios.tsx automatically parse these JSON strings before passing to consumers.

Dictionary Completeness Pattern

When passing dictionaries from Objective-C++ to Swift, always include all fields even if empty:

// Always include all fields, use @"" for empty values
representation[@"urlLabel"] = !col.representation.urlLabel.empty() 
  ? RCTNSStringFromString(col.representation.urlLabel) 
  : @"";

This ensures Swift's JSONDecoder receives all expected fields, preventing missing data in decoded structs.

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT