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.
interfaceandtypedescribe 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.
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?.breturnsundefinedinstead of throwing whenais null or undefined.a ?? bfalls back tobonly whenais null or undefined.- Why
- API data and route params are often missing. These operators read nested values safely, and
strictNullChecksmakes TypeScript insist you handle the gap.
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 backWatch 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
...,maporfilter. 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.
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.
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.
asyncfunctions return Promises andawaitpauses until one settles.Promise.allruns 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.
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.
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
importandexport. Third-party packages are installed from npm and recorded inpackage.json. - Why
- In an Expo project,
npx expo installpicks package versions that match your Expo SDK, which avoids native crashes from mismatched libraries.
npx create-expo-app@latest my-app
npx expo install expo-camera
npm install zustand
npx expo-doctorWatch out
Installing native libraries with plain npm install can pull a version that does not match your SDK; use npx expo install.