view react_games/README.md @ 59:e06bc03d9618

[Color Game] Making game with a friend.
author June Park <parkjune1995@gmail.com>
date Sat, 20 Dec 2025 10:53:13 -0800
parents 9b8521f75409
children
line wrap: on
line source


# Introduction

Hi, my name is June I am a software engineer at Meta working on ads manager. I work with anything related to destination and placements of the ads.

# Loading is slow — what do you do?

- Three pillor
  - too long to download hmtl, js and CSS;
  - too long to donwload assets;
  - TFBL is too long

{
**Me:**
“First I figure out which bucket we’re in: **too many bytes**, **heavy media**, or **slow first byte**.

If it’s **JS bytes**, I split aggressively: route chunks, `React.lazy`, and I do *render-as-you-fetch* so data and code arrive together. At Meta we declared EntryPoints so the bundler knew exactly what to ship for, say, a modal—no more shipping the whole app for a tiny flow.

If it’s **media**, I’ve led pipelines that convert images to WebP/AVIF with proper `srcset` and generate multi-bitrate **HLS**. Short 2-second segments, aligned keyframes, quick start. I even shipped a lightweight player tuned for iPad because that’s what our artists used. EXIF

If it’s **TTFB**, I check edge caching and geography. I once found an Nginx box serving North America from Europe—moved it and TTFB dropped immediately.

I keep bundle budgets in CI; I’ve even split a ‘kitchen-sink utils.ts’ into per-export files to stop accidental pulls. And I won’t merge if LCP or JS bytes regress.”
}
---

# How do you do i18n/l10n?

- ICU

{
**Me:**
“I externalize every user string and use ICU (International Component for unicode) style messages for plurals, select(gender, status), number, and datatime in curly brackets. I believe JS the most common one is called format.js and it just follow common ICU format while you need to set a locale. For react, you can do that as a global state or looking at your browser.

At Meta we used FBT: annotate strings, extract, translators ship locale payloads, runtime picks based on browser locales. For RTL, I prefer **logical CSS properties**—`margin-inline-start` instead of `margin-left`—so the layout flips automatically. Icons and chevrons mirror; no string concatenation—everything is a template.”

**TL;DR:** “Template everything, lazy-load locales, pseudolocalize, and design with RTL in mind.”
**If they drill down:** “I’ll show how `dir` + logical props remove most RTL branches.”
}
---

# How do you ensure accessibility?

{
For me, accessibility starts with semantics. If HTML already has the right element—like <button>, <nav>, <main>—I’ll use that first. ARIA is my backup plan, not my starting point. And keyboard support isn’t optional: I make sure there’s visible focus, a logical tab order, and if there’s a dialog, I trap focus inside and return it to the trigger when it closes.

One example: I worked on a government-funded site for the Canadian Digestive Health Foundation. Accessibility was part of the core requirements, so I built a workflow where axe-core and Lighthouse audits ran in CI on every commit. We also did manual screen reader spot checks with VoiceOver on macOS and NVDA on Windows. That caught a few big issues early—like ‘div-as-button’ anti-patterns and subtle color contrast failures that automated tools flagged but designers had missed.

In React, I try to keep the DOM lean for both performance and assistive tech—so I avoid unnecessary wrapper <div>s and prefer fragments. A smaller DOM actually makes it faster for screen readers to parse and navigate, which we saw in our own testing. At Google, I saw firsthand how small DOM and strong semantics reduced not just accessibility bugs, but also made features easier to test and maintain, because you’re working with real HTML roles, not bolted-on ARIA roles that can drift out of sync.
}
---

# How do you improve web app security?

{
My security mindset is: all input is hostile, all output is untrusted.

For XSS, I never render raw HTML directly. If I absolutely have to—say for trusted markdown rendering—I sanitize it with a vetted library and pair it with a tight Content Security Policy. I like using nonces for inline scripts so even if HTML injection happens, the injected scripts won’t run.

For CSRF, if I’m using cookie-based auth, I always set SameSite to Lax or Strict and pair it with CSRF tokens. Frameworks like Rails and Django handle this elegantly, and I’ve shipped secure implementations in both. I also make sure state-changing requests (POST, PUT, DELETE) are protected while GETs remain idempotent.

Token storage is strictly in httpOnly cookies—never localStorage—so they can’t be accessed via JavaScript if there’s an XSS bug. I learned the hard way that storing them in localStorage makes them trivial to exfiltrate once an attacker has script execution.

I’ve been burned by path traversal before while building a C server. A crafted ../../etc/passwd path slipped past my naive checks. Now I whitelist file roots, normalize paths, and reject anything that resolves outside of the intended directory before even touching the filesystem.

Then there are the defensive headers I always set:

Strict-Transport-Security (HSTS) to enforce HTTPS

X-Frame-Options: DENY to stop clickjacking

X-Content-Type-Options: nosniff to prevent MIME sniffing
// so they don't upload script and get executed.

Finally, I keep the supply chain clean—running dependency scans in CI to flag vulnerable packages before they hit production.
}
### Anything special for LLM apps?

{
**Me:**
“Yes—treat model output as **untrusted**. Sanitize before rendering (no `javascript:` links, strip HTML). Tools run in **containers** with least privilege; dangerous commands require explicit user approval. Retrieval is allow-listed; prompts redact secrets/PII. We log, rate-limit, and run output validators so a jailbreak can’t inject code into the UI.”

**TL;DR:** “Sandbox tools, sanitize outputs, least privilege, and audit everything.”
}
---

# Cross-browser compatibility?
{
I start cross-browser compatibility work with a clear, telemetry-driven support policy—usually defined in Browserslist based on actual analytics data from production traffic. That way, we only transpile and polyfill for the browsers our users actually have, which keeps bundles smaller and startup faster. Babel + core-js handle the JS side automatically, but the output is scoped to what Browserslist specifies.

Testing happens continuously—we run functional and visual regression tests on Chrome, Safari, and Firefox as part of our CI pipeline. I like visual diffs triggered per key user action, because layout breakage often slips past unit tests but is obvious in a screenshot comparison.

One real-world example: a teammate introduced ES6 Set in a shared util, but one of our long-tail browsers didn’t support it. Instead of shipping core-js globally, I implemented a scoped polyfill with feature detection—if (!window.Set) { loadPolyfill(); }—so the fix applied only where needed.

On the CSS side, I start with Normalize.css to flatten default browser inconsistencies, and use Autoprefixer to add only the vendor prefixes relevant to our supported browsers. That’s been especially helpful with Flexbox gap support and CSS Grid alignment quirks.

Safari gets extra scrutiny because it often behaves differently even when standards-compliant:

Viewport units — I account for iOS’s dynamic toolbars by using svh/lvh fallbacks and calc()-based adjustments for older versions.

Media autoplay — I handle the playsinline attribute, preload strategies, and gesture requirements so video/audio works predictably across iOS and macOS.

In short, I aim for telemetry-driven browser targeting, surgical polyfills, consistent tri-browser test coverage, and Safari-specific hardening—keeping the experience stable without bloating the codebase."
}

---

# React: state management — what do you recommend?

**Me:**
{
  Local UI state → I stick with useState or useReducer. They’re built-in, predictable, and perfect for component-specific concerns like form inputs, modals, or toggles. If a component’s state is self-contained, there’s no reason to lift it up or introduce another library.
  
  Cross-component state that’s small and read-heavy → I’ll reach for React Context. It’s great for things like theme, auth user info, or feature flags. I keep writes infrequent here, because context updates re-render all consumers; if writes get frequent, I move to a store.
  
  Server state → This is where React Query or SWR shine. They give me caching, stale-while-revalidate behavior, optimistic updates, background refresh, and request deduplication out of the box. I treat server state as derived—meaning if the server has the truth and I can fetch it, I don’t duplicate it in a client store.
  
  Global, complex client state → Zustand or Redux Toolkit. Zustand is my go-to for minimal boilerplate and small to medium stores. Redux Toolkit comes out when I need the extras: time travel, action logging, serializable state for persistence or debugging, and strong devtools integration.
  
  Graph-shaped data → Relay or Apollo if the backend is GraphQL. Relay’s compiler is fantastic for large apps with strict data dependencies; Apollo is quicker to adopt in smaller or mixed-stack projects.
}

---

# Can you explain `useRef` and when you use it?

{
"useRef is essentially a persistent, mutable container that survives re-renders without causing re-renders.
It returns an object like { current: … } where current can hold any value—DOM elements, numbers, objects, even functions. Unlike useState, updating .current doesn’t trigger a component update, so it’s perfect for data you want to remember but that doesn’t affect rendering directly.

Common uses in my code:

DOM access — For example, ref={inputRef} on an <input> so I can call inputRef.current.focus().

Timers and debouncers — Storing a timeout ID so it survives between renders but won’t trigger a re-render when it changes.

Previous values — Capturing something like prevCount.current = count inside an effect to compare with the next render.

AbortController — Keeping an instance between renders to cancel pending fetch requests when the component updates or unmounts.

Bridging to non-React libraries — Many third-party libraries expect a stable reference to mutate; useRef is the glue to make that work without messing up React’s render cycle.

I think of useRef as "state that lives outside React’s rendering flow"—it’s there when you need memory across renders, but you don’t want it to cause a re-render when it changes.

TL;DR: “It’s a stable box for remembering values across renders without re-rendering.”
}


---

# Component vs Hook?

{
React components take props and return a React element. Custom hooks can take in anything and return anything. Built in React hooks like useState allow us to hook into the React lifecycle.
}
---

# Pros and cons of React?
{
  React’s strengths start with its composable model—UI is just functions of state, which makes it easy to break complex interfaces into small, reusable components. The ecosystem is enormous, so there’s a mature library or pattern for almost anything: routing, data fetching, animation, state management. And with the newer SSR + streaming APIs, React’s server story is finally first-class—no more ‘SSR as an afterthought.’ On top of that, TypeScript support has matured to the point where DX is excellent—generics, discriminated unions, and inferred props make large codebases much safer.
  
  But there are trade-offs. Hydration can be expensive if you over-hydrate everything the server sends—your Time to Interactive can balloon. It’s also easy to lean too heavily on Context for shared state, which triggers unnecessary re-renders and hurts performance. And React’s concurrent rendering features, while powerful, add mental overhead: you have to think about transitions, tearing, and how updates are prioritized.
  
  I mitigate those issues by being intentional about where React runs:
  
  Islands / partial hydration so only interactive parts hydrate; the rest stays static HTML.
  
  Memoization where it matters—React.memo, useMemo, and useCallback—but only after profiling so I don’t add useless complexity.
  
  useSyncExternalStore for stable, low-overhead subscriptions to global state without causing render cascades.
  
  Code-splitting at the route, component, or even chunk-of-feature level so users never download more than they need.
}

---

# Your ideal stack for a Copilot-style app?

{
**Me:**
“Go on the backend with FastCGI—render the shell with SSR for instant TTV, sprinkle React **islands** where I need interactivity. Postgres + Redis. An LLM service that proxies providers, enforces guardrails, and sandboxes tools. CDN for static/media, edge cache for SSR fragments, and SSE/WebSockets for streaming tokens.”

**TL;DR:** “Go SSR + React islands; Postgres/Redis; sandboxed LLM service; CDN + edge.”
}