Mobile development: React Native

Sefism.

Chapter 01

JavaScript and TypeScript essentials

The JavaScript and TypeScript that React Native code depends on: types, nullish values, immutable updates, closures and async code.

Types, interfaces and unions

Also called union type

What
TypeScript adds static types to JavaScript. interface and type describe object shapes, ? marks optional fields and a union like 'idle' | 'loading' limits a value to a few options.
Why
Typed props, API responses and navigation params catch the "undefined is not an object" crash in the editor instead of on a user's phone.
TSX
interface User {
  id: string;
  name: string;
  avatarUrl?: string;
}

type Status = 'idle' | 'loading' | 'error';

function label(user: User, status: Status): string {
  return status === 'loading' ? 'Loading...' : user.name;
}

Watch out

Casting API data with as User does not check it at runtime; validate untrusted JSON with a schema library such as Zod.

Optional chaining and nullish coalescing

Also called ?., ??, optional chaining, nullish coalescing, non-null assertion

What
a?.b returns undefined instead of throwing when a is null or undefined. a ?? b falls back to b only when a is null or undefined.
Why
API data and route params are often missing. These operators read nested values safely, and strictNullChecks makes TypeScript insist you handle the gap.
TSX
const city = user.address?.city ?? 'Unknown';

const count = 0;
const a = count || 10; // 10, because 0 is falsy
const b = count ?? 10; // 0, only null or undefined fall back

Watch out

Using || for defaults replaces valid values like 0, '' and false; use ??.

Immutable updates with spread

Also called spread operator, destructuring, immutable update, shallow copy

What
Instead of changing an array or object in place, you create a new one with the spread operator ..., map or filter. Destructuring pulls fields out: const { name } = user.
Why
React decides whether to re-render by comparing references. Mutating state keeps the same reference, so the screen does not update.
TSX
const [todos, setTodos] = useState<Todo[]>([]);

// Wrong: same array, React sees no change
todos.push(newTodo);

// Right: a new array or object
setTodos((prev) => [...prev, newTodo]);
setTodos((prev) =>
  prev.map((t) => (t.id === id ? { ...t, done: true } : t))
);

Closures and stale values

Also called stale closure, closure

What
A closure is a function that remembers the variables from where it was created. In React, each render creates new closures that see that render's state.
Why
Timers, event listeners and effects capture state at the moment they were created. Understanding this explains most "my state is always the old value" bugs.
TSX
function Timer() {
  const [seconds, setSeconds] = useState(0);
  useEffect(() => {
    const id = setInterval(() => {
      setSeconds((s) => s + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);
  return <Text>{seconds}s</Text>;
}

Watch out

Writing setSeconds(seconds + 1) inside the interval reads the stale seconds from the first render; use the updater form.

Promises and async/await

What
A Promise represents a result that arrives later. async functions return Promises and await pauses until one settles. Promise.all runs several at once.
Why
Every fetch, storage read and permission request in React Native is asynchronous, so correct error handling here decides whether a failure shows a message or a blank screen.
TSX
async function loadDashboard() {
  try {
    const [user, feed] = await Promise.all([
      getUser(),
      getFeed(),
    ]);
    return { user, feed };
  } catch (err) {
    console.error('Dashboard failed', err);
    throw err;
  }
}

Watch out

A Promise without await or .catch swallows its error as an unhandled rejection.

Discriminated unions

What
A union of object types that share one literal field, such as status. Checking that field narrows the type, so TypeScript knows which other fields exist.
Why
It is the cleanest way to model loading, success and error states: impossible combinations like "loading with an error message" cannot be expressed.
TSX
type LoadState =
  | { status: 'loading' }
  | { status: 'success'; posts: Post[] }
  | { status: 'error'; message: string };

function Feed({ state }: { state: LoadState }) {
  switch (state.status) {
    case 'loading': return <Spinner />;
    case 'success': return <PostList posts={state.posts} />;
    case 'error': return <Text>{state.message}</Text>;
  }
}

Modules and npm

Also called ES modules, package.json, npm

What
Each file is a module that uses import and export. Third-party packages are installed from npm and recorded in package.json.
Why
In an Expo project, npx expo install picks package versions that match your Expo SDK, which avoids native crashes from mismatched libraries.
Shell
npx create-expo-app@latest my-app
npx expo install expo-camera
npm install zustand
npx expo-doctor

Watch out

Installing native libraries with plain npm install can pull a version that does not match your SDK; use npx expo install.

01 · JavaScript and TypeScript essentials1 / 8