Representative practice prompts
Five JPMorgan Chase frontend interview practice questions
For each prompt, clarify the contract, explain ownership and trade-offs, implement or diagram the smallest credible slice, and test the failure cases. Each prompt links to the public report it is modelled on and to a practice page.
Prompt 1
Write a generic memoization function inside a security-and-React round
In a 60-minute round that also covers XSS, security principles at work, React lifecycle methods and component design for data privacy, write memoize(fn): a wrapper that caches results by argument so a repeated call with the same input returns the cached value without calling fn again, then explain when it is safe to use.
Practice version modelled on a reported round: Public report: frontendgeek SDE2 frontend loop, Bengaluru, April 2024
What this measures
Cache-key design, reference versus value equality, memory growth and eviction, the pure-function precondition, and whether you can move from security concepts to code without losing the thread.
Clarify first
- Confirm how arguments are compared: one primitive, several primitives, or object references, and whether the this value matters for the wrapped function.
- Confirm whether the cache may grow without bound in the exercise or needs a size limit or an eviction rule.
Strong answer should cover
- Build the key deliberately: a Map keyed by the single argument in the simple case, nested Maps or a serialised tuple for several, and say why JSON.stringify on object arguments is a trade-off rather than a default.
- Keep the cache in a closure per wrapped function, pass this and the full argument list through on a miss, and return the cached value on a hit.
- State the precondition out loud: memoization is correct only for pure functions with stable outputs, and cached rejections or thrown errors need a decision.
- Name the memory cost and offer a bounded cache or a WeakMap for object keys when the interviewer asks about growth.
Common miss: Keying each call with JSON.stringify, which collides on undefined, functions and key order, and leaving the cache to grow for the life of the page without saying so.
Practice the memoize function (Premium) Prompt 2
Write a polyfill for the reduce function, with promises, generators and currying as follow-ups
In a JavaScript and framework round, implement Array.prototype.reduce without using the native method: call the reducer with the accumulator, the current value, the index and the array for existing indices only, support an optional initial value, and throw on an empty array with no initial value. Expect the same round to move on to promises and asynchronous behaviour, generator functions, object manipulation, pure functions and currying.
Practice version modelled on a reported round: Public report: frontendgeek SDE2 frontend loop, Bengaluru, April 2024
What this measures
Exact knowledge of a core array method, handling of sparse arrays and edge cases, and breadth across the JavaScript topics the round moves through after the code.
Clarify first
- Confirm whether the polyfill goes on Array.prototype or is a standalone function, and whether holes in sparse arrays must be skipped as the native method skips them.
- Confirm the expected behaviour for an empty array without an initial value; the native method throws a TypeError.
Strong answer should cover
- Detect whether an initial value was passed by the arguments length, not by comparing with undefined, so undefined stays a valid initial value.
- Without an initial value, take the first existing element as the accumulator and start from the next index; skip holes with an in check.
- Call the reducer with (accumulator, currentValue, index, array), return the final accumulator, and throw a TypeError for an empty array with no initial value.
- For the follow-ups, explain microtask ordering for promises, how a generator pauses at yield and resumes on next(), and show currying as a closure that collects arguments until the arity is met.
Common miss: Treating undefined as no initial value, iterating over holes in a sparse array, and returning undefined for an empty array instead of throwing.
Practice implementing Array.prototype.reduce (Premium) Prompt 3
Live-code a generic async promise React hook with typed state and effect cleanup
Write useAsync<T>(fn: () => Promise<T>, deps) in TypeScript without references: it runs the async function, exposes loading, data and error, re-runs when the dependencies change, and uses the useEffect cleanup function so a response that arrives after the component unmounted or the inputs changed does not reach state.
Practice version modelled on a reported round: Public report: Aced senior frontend engineer loop, UK, around May 2026
What this measures
Generic types at a hook boundary, a request-state model that cannot contradict itself, effect lifecycle and cleanup, and stale-response handling while you narrate.
Clarify first
- Confirm whether the hook runs on mount only or whenever the dependencies change, and whether a manual re-run function is expected.
- Confirm what cancellation must guarantee: ignoring a late result inside the hook, or also aborting the request with an AbortSignal.
Strong answer should cover
- Type the hook with a generic parameter for the resolved value and return a discriminated union (idle, loading, success with data, error with an Error), so callers narrow on status rather than on three optional fields.
- Start each run by setting loading, then await inside the effect; keep a cancelled flag or an AbortController in the effect scope and flip it in the cleanup function.
- Check the flag before each setState after the await, so an unmounted component or a superseded run does not write state.
- Normalise unknown rejections to an Error and keep the dependency list honest; if the function identity changes on each render, say so and take the dependencies explicitly.
Common miss: A hook that sets state after the await with no cleanup, so a slower earlier request overwrites a newer result after the inputs changed.
Practice fetching and rendering with loading, error and empty states (free) Prompt 4
Build a to-do list where each task has a priority that can be changed, and tasks can be deleted
In React, build a to-do list where each task carries a high, medium or low priority. The priority can be updated after the task is created, and a whole task can be deleted. Keep the input controlled, the ids stable and the updates immutable.
Practice version modelled on a reported round: Public report: Taro React developer loop, Houston, June 2022
What this measures
Local state modelling, controlled inputs, immutable updates of an array of objects, stable keys, and whether the priority control works from the keyboard.
Clarify first
- Confirm whether tasks are sorted or grouped by priority, and the default priority for a new task.
- Confirm whether completion state or persistence is in scope, or only add, change priority and delete.
Strong answer should cover
- Hold tasks as an array of objects with a stable id, text and priority, and derive a sorted or grouped view at render time rather than storing it.
- Update a priority by mapping to a new array with one replaced object; delete by filtering; leave the existing array and objects untouched.
- Render the priority as a labelled select or radio group so it is keyboard-operable and announced; use the id as the key, not the index.
- Keep the input controlled, clear it on submit, ignore empty or whitespace-only text, and show an empty state when the list is empty.
Common miss: Using the array index as the key, so deleting a task shifts the priority controls of the tasks after it, or mutating a task object in place so React does not re-render.
Practice the React to-do list (free) Prompt 5
Hold the React and JavaScript conversation round: closures, clones, hooks, storage and CORS
A technical conversation, in some accounts an hour with two interviewers, that moves through fundamentals: explain a closure; shallow versus deep clone and an Object.assign use case; useCallback versus useMemo; useState versus useRef; context and when a higher-order component is the better tool; the difference between a cookie, sessionStorage and localStorage; what CORS is. Answer each in two or three exact sentences with one example.
Practice version modelled on a reported round: Public report: Blind React software engineer interview, London, May 2023
What this measures
Whether you can state fundamentals plainly, pick the right example quickly, and move between JavaScript, React and browser topics without drifting into internals the interviewer did not ask about.
Clarify first
- Ask whether the interviewer wants a definition or an example first; a short example usually proves understanding fastest.
- Confirm the role's framework and React version before the hook questions, since the memoization answer depends on it.
Strong answer should cover
- Closure: a function keeps access to the variables of the scope it was created in after that scope has returned; give the counter example. Shallow clone (spread, Object.assign) copies the top level and shares nested references; deep clone (structuredClone) copies the whole graph; Object.assign also merges defaults into a target.
- useMemo caches a computed value and useCallback caches a function identity, both for referential stability between renders; useState schedules a re-render on change while useRef holds a mutable value that does not.
- Context shares a value down the tree without prop drilling; a higher-order component wraps a component to add behaviour and fits when the same cross-cutting logic must apply to several components.
- Cookies travel with requests to the server and can be HttpOnly; sessionStorage lives per tab until it closes; localStorage persists per origin. CORS is the browser rule that blocks cross-origin reads unless the server allows them with Access-Control headers.
Common miss: Long answers about internals that do not state the simple rule, or saying useCallback makes the function faster instead of explaining referential stability.
Review closures (free)