Runtime Crash from Uninitialized State Arrays
Open the screen and the app crashed. Refresh and it worked. Sound familiar?
The Problem
When users opened a certain screen for the first time, the app sometimes threw a runtime error: something like "cannot read property 'map' of undefined." The screen was supposed to show a list, but instead it crashed. After a refresh it often worked, which suggested the problem was timing: the list was not ready on the first render.
Investigation
I looked at the component that rendered the list. It had state for the list and called a fetch on mount. In the render path it did something like items.map(...) or items.filter(...). The initial state for items was not set: it was just useState() or useState(undefined), so before the fetch completed, items was undefined. The component did not check for that and tried to call .map on undefined, which caused the crash.
In other parts of the codebase we had initialised list state as an empty array, e.g. useState([]). So the pattern was inconsistent: some components started with [] and were safe, others assumed the data would be there and crashed when it was not. This screen had been written assuming the list would always be an array once the component was visible, but the state started as undefined and was only set when the async load finished.
Root Cause
The state for the list was never initialised to an array. So on the first render, before the fetch resolved, the value was undefined. Any code that treated it as an array (e.g. .map, .length, .filter) threw. The root cause was missing defensive initialisation: the component assumed the list was always an array but did not guarantee that in the initial state.
The Fix
We initialised the list state with an empty array: useState([]). That way, on the first render and until the fetch completed, the component had a valid array to work with. It could render an empty state (e.g. "Loading..." or an empty list) and then update when the data arrived. We did not need to add a lot of null checks; we just made the initial state valid.
We also did a quick pass on similar components that held "list" or "items" state and ensured they started with [] instead of undefined when the value was always expected to be an array. That prevented the same class of bug elsewhere.
Lessons Learned
- If state will eventually hold an array, initialise it as
[]so that every render can safely call array methods. Avoid leaving it undefined unless you explicitly handle the undefined case in the render path. - "It works after refresh" often means the first render is the problem: something is undefined or not ready yet. Initial state and loading states need to be part of the design.
- A small convention (e.g. "list state starts as []") applied across the codebase reduces whole categories of runtime errors. Consistency here is worth the extra line.
Have thoughts on this story or questions? Get in touch.