React
JavaScript
Error Handling
Programming
Web Development

Warning Failed child context type Invalid child context 'virtualizedCell.cellKey' of type 'number' supplied to 'CellRenderer', expected 'string'

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

This React Native warning appears when list cell keys are numbers while internal list components expect strings. The UI may still render, but unstable key types can cause incorrect recycling behavior, flicker, and hard-to-reproduce rendering bugs. Fixing key generation early prevents noisy logs and improves list stability.

Why the Warning Happens

FlatList and related virtualized components depend on stable string keys to track items across renders. If your keyExtractor returns a number, React Native warns that virtualizedCell.cellKey type is invalid for CellRenderer.

tsx
1import React from 'react';
2import { FlatList, Text, View } from 'react-native';
3
4const data = [
5  { id: 1, name: 'Ada' },
6  { id: 2, name: 'Lin' },
7];
8
9export default function App() {
10  return (
11    <View>
12      <FlatList
13        data={data}
14        keyExtractor={(item) => String(item.id)}
15        renderItem={({ item }) => <Text>{item.name}</Text>}
16      />
17    </View>
18  );
19}

The important part is String(item.id). Converting explicitly keeps key type correct and consistent.

Key Quality Rules for Virtualized Lists

Good keys should be:

  • Stable across re-renders.
  • Unique across all visible items.
  • Derived from item identity, not array index.

Using array index often works at first, but it breaks when items are inserted, removed, or reordered.

tsx
keyExtractor={(item) => String(item.id)}

If your backend does not provide IDs, create deterministic keys once during data normalization and reuse them.

Normalize Data at the Boundary

A robust pattern is to normalize server payloads before rendering. This keeps UI components simple and avoids repetitive type conversions in each list.

typescript
1type RawUser = { id: number; name: string };
2type User = { id: string; name: string };
3
4function normalizeUsers(rows: RawUser[]): User[] {
5  return rows.map((row) => ({
6    id: String(row.id),
7    name: row.name,
8  }));
9}
10
11const users = normalizeUsers([
12  { id: 10, name: 'Sam' },
13  { id: 11, name: 'Nia' },
14]);

With this approach, the view layer never receives numeric keys and warnings disappear consistently.

Debugging Checklist

When warning persists:

  • Verify every list component has a keyExtractor.
  • Confirm extracted key type is string in runtime logs.
  • Check nested lists where inner keys may still be numeric.
  • Ensure memoized item components are not mutating key-related props.

You can add a temporary assertion in development builds.

typescript
1function assertStringKey(key: unknown): string {
2  if (typeof key !== 'string') {
3    throw new Error('List key must be a string');
4  }
5  return key;
6}

This catches incorrect keys early during testing.

Handling Composite Keys Safely

Some datasets require composite keys, such as account id plus item id. In that case, build a deterministic string key with stable parts and clear separators.

tsx
keyExtractor={(item) => `${String(item.accountId)}:${String(item.itemId)}`}

This prevents collisions and ensures list identity remains stable across pagination and refresh events.

Performance Considerations in Large Lists

Key generation should be cheap and deterministic. Avoid expensive hash generation per render. If key composition is heavy, precompute keys when data enters state.

typescript
1type Row = { id: number; title: string; key?: string };
2
3function prepareRows(rows: Row[]): Row[] {
4  return rows.map((row) => ({
5    ...row,
6    key: String(row.id),
7  }));
8}

Then use item.key directly in keyExtractor. This keeps render paths fast and predictable.

Testing Strategy

Write one test that validates key type and uniqueness for representative payloads. Even simple tests catch common regressions from API shape changes.

typescript
1function validateKeys(rows: Array<{ id: number }>) {
2  const keys = rows.map((r) => String(r.id));
3  const unique = new Set(keys);
4  if (keys.length !== unique.size) {
5    throw new Error('duplicate keys detected');
6  }
7}
8
9validateKeys([{ id: 1 }, { id: 2 }]);

This check is cheap and helpful in CI.

Common Pitfalls

  • Returning item.id directly when id is numeric.
  • Using array index as key in lists that reorder or paginate.
  • Fixing only one list while nested lists still emit numeric keys.
  • Creating random keys per render and forcing unnecessary remounts.
  • Ignoring warnings until rendering bugs appear in production.

Summary

  • Virtualized list keys should be stable strings.
  • Convert numeric identifiers using String in keyExtractor.
  • Prefer identity-based keys over array index keys.
  • Normalize payload data so key types are correct before rendering.
  • Use assertions and targeted logs to catch key regressions early.

Course illustration
Course illustration

All Rights Reserved.