- https://leetcode.com/problems/two-sum/ (Easy)
- https://leetcode.com/problems/contains-duplicate/ (Easy)
- https://leetcode.com/problems/design-browser-history/ (Medium)
- https://leetcode.com/problems/top-k-frequent-elements/ (Medium)
- https://leetcode.com/problems/word-break/ (Medium)
- https://leetcode.com/problems/merge-intervals/ (Medium)
Promise/async
Modify main
function to use async/await
instead of Promise
. Do not modify the asyncCalculate
function.
// Do not modify this function
function asyncCalculate(a, b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (isNaN(a) || isNaN(b)) {
reject(new Error('a or b is not a number'));
} else {
resolve(a + b);
}
}, 1000);
});
}
// Update the below function
function main() {
asyncCalculate(1, 2).then(result => {
console.log(result);
}).catch(error => {
console.log(error);
});
}
Expected result:
async function main() {
try {
const result = await asyncFunction(1, 2);
console.log(result);
} catch (error) {
console.log(error);
}
}
Bonus questions:
- What different between Promise and async function? (Expected: async function can work well in loops, eg: resolve array of promises one by one)
- How to wait for multiple promises? (Expected: use Promise.all or Promise.allSettled)
Promise/async (Advance)
Modify the below class to achieve the expected behaviour:
// Update the below class
class Chaining {
eat() {
}
sleep() {
}
work() {
}
}
new Chaining().eat().sleep(2).work()
// Expected: print eat, sleep for 2 seconds, print work
// *sleep for 2 seconds mean print nothing in 2 seconds
new Chaining().sleep(2).eat().work().sleep(1).eat()
// Expected: sleep for 2 seconds, print eat, print work, sleep for 1 seconds, print eat
Expected result:
class Chaining {
taskQueue = Promise.resolve()
addTask = (f) => {
this.taskQueue = this.taskQueue.then(f)
}
eat() {
this.addTask(() => console.log('eat'))
return this
}
work() {
this.addTask(() => console.log('work'))
return this
}
sleep(s) {
this.addTask(
() =>
new Promise((r) => {
setTimeout(() => {
r()
}, s * 1000)
})
)
return this
}
}
// new Chain().eat().work().sleep(2).work().eat().sleep(1).work()
Closure
Write a useState
function that can do the following:
// Modify the below function
function useState(initialState) {
// do something
}
// Do not modify the below function
function main() {
const [state, setState] = useState(0);
console.log(state()); // returns 0
setState(1);
console.log(state()); // returns 1
setState(2);
console.log(state()); // returns 2
}
Expected result:
function useState(initValue) {
let state = initValue;
function getState() {
return state;
}
function setState(newValue) {
state = newValue;
}
return [getState, setState];
}
Currying (closure advanced)
Currying is a transformation of functions that translates a function from callable as f(a, b, c) into callable as f(a)(b)(c).
Currying does not call a function. It just transforms it.
Write a curry
function that can do the following:
// Modify the below function
function curry(f) {
// do something
}
// Do not modify the below function
function main() {
const sum = (a, b) => {
return a + b;
};
const curriedSum = curry(sum);
curriedSum(1)(2); // returns 3
}
Expected result:
function curry(f) {
return function(a) {
return function(b) {
return f(a, b);
};
};
}
Memoization
Write a function that can memoize the result of a function:
// Modify the below function
function memoize(func) {
// do something
}
// Do not modify the below function
function main() {
const func = (a, b) => {
console.log('Doing some calculation');
return a + b;
};
const memoized = memoize(func);
memoized(1, 2); // returns 3
memoized(1, 2); // returns 3 (from cache)
memoized(1, 3); // returns 4
memoized(1, 3); // returns 4 (from cache)
}
Expected result:
function memoize(func) {
const cache = {};
return function(...args) {
if (cache[args]) {
return cache[args];
}
const result = func(...args);
cache[args] = result;
return result;
}
}
This context
Given the following code:
const Animal = (name, type) => {
this.name = name;
this.type = type;
this.age = 0;
}
Animal.prototype.birthday = function () {
this.age++;
}
const leo = new Animal('Leo', 'Lion');
// Run -> Uncaught TypeError: Cannot set properties of undefined (setting 'birthday')
Explanation:
Arrow functions don’t have their own this
. This leads to three errors in our code.
First, we’re adding properties to this
in the constructor function. Again, because Arrow Functions don’t have their own this
, you can’t do that.
Second, we can’t use the new
keyword with an Arrow Function. This will throw a X is not a constructor
error.
Third, we can’t add a property on a function’s prototype if that function is an arrow function, again, because there’s no this
.
Solution:
function Animal (name, type) {
this.name = name;
this.type = type;
this.age = 0;
}
Animal.prototype.birthday = function () {
this.age++;
}
const leo = new Animal('Leo', 'Lion');
Scoping issue
Given the following code:
function todoReducer(state, action) {
switch (action.type) {
case "ADD_TODO":
const todos = state.todos;
return { ...state, todos: todos.concat(action.payload) };
case "REMOVE_TODO":
const todos = state.todos;
const newTodos = todos.filter(todo => todo.id !== action.id);
return { ...state, todos: newTodos };
default:
return state;
}
}
Explanation:
The todos
variable is being re-declared within the same block. Variables declared with const
and let
are block-scoped, meaning they don’t exist outside of the block they were called in. Blocks are created with {}
brackets around if/else
statements, loops, and functions.
There are many solutions, including changing the variable name in the different cases or removing the variable altogether. We can also wrap each of our cases in a block to isolate the variables.
Solution:
function todoReducer(state, action) {
switch (action.type) {
case "ADD_TODO": {
const todos = state.todos;
return { ...state, todos: todos.concat(action.payload) };
}
case "REMOVE_TODO": {
const todos = state.todos;
const newTodos = todos.filter((todo) => todo.id !== action.id);
return { ...state, todos: newTodos };
}
default:
return state;
}
}
- Write a CSS class that put a
div
tag center in both the horizontal and vertical axis.
Counter (basic state test)
Write a Component that will take 2 props as below
import React, { useState } from "react";
export default function Timer({ total = 0, onTimerFinish }) {
const [count, setCount] = useState(total);
return <>{count}</>;
}
total
: the total number of times the timer should count downonTimerFinish
: a function that will be called when the timer finishes- The component should have a
count
state that starts attotal
- Every second, the component should decrement the
count
state by 1 - When finished, the component should call the
onTimerFinish
function
Expected result:
export default function Timer({ total = 0, onTimerFinish }) {
const [count, setCount] = useState(total);
useEffect(() => {
const myInterval = setInterval(() => {
if (count > 0) {
setCount(count - 1);
}
if (count === 0) {
if (onTimerFinish) {
onTimerFinish();
}
clearInterval(myInterval);
}
}, 1000);
return () => {
clearInterval(myInterval);
};
}, [count, onTimerFinish]);
return <>{count}</>;
}
Dual Counter ("isolate re-render" knowledge test)
Write a component to render 2 counters and 2 buttons:
function CountButton({ onClick, count }) {
return <button onClick={onClick}>{count}</button>
}
function DualCounter() {
const [count1, setCount1] = React.useState(0)
const increment1 = () => setCount1(c => c + 1)
const [count2, setCount2] = React.useState(0)
const increment2 = () => setCount2(c => c + 1)
return (
<>
<CountButton count={count1} onClick={increment1} />
<CountButton count={count2} onClick={increment2} />
</>
)
}
Every time you click on either of those buttons, the DualCounter
's state changes and therefore re-renders which in turn will re-render both of the CountButton
. However, the only one that actually needs to re-render is the one that was clicked right? So if you click the first one, the second one gets re-rendered, but nothing changes. We call this an "unnecessary re-render."
Most of the time you should not bother optimizing unnecessary re-renders. React is very fast and there are so many things I can think of for you to do with your time that would be better than optimizing things like this.
However, there are situations when rendering can take a substantial amount of time (think highly interactive Graphs/Charts/Animations/etc.). Thanks to the pragmatistic nature of React, there's an escape hatch:
const CountButton = React.memo(function CountButton({ onClick, count }) {
return <button onClick={onClick}>{count}</button>
})
Now React will only re-render CountButton
when its props change! Woo! But we're not done yet. Remember that whole referential equality thing? In the DualCounter
component, we're defining the increment1
and increment2
functions within the component functions which means every time DualCounter
is re-rendered, those functions will be new and therefore React will re-render both of the CountButton
s anyway.
So this is the other situation where useCallback
and useMemo
can be of help:
const CountButton = React.memo(function CountButton({ onClick, count }) {
return <button onClick={onClick}>{count}</button>
})
function DualCounter() {
const [count1, setCount1] = React.useState(0)
const increment1 = React.useCallback(() => setCount1(c => c + 1), [])
const [count2, setCount2] = React.useState(0)
const increment2 = React.useCallback(() => setCount2(c => c + 1), [])
return (
<>
<CountButton count={count1} onClick={increment1} />
<CountButton count={count2} onClick={increment2} />
</>
)
}
Now we can avoid the so-called "unnecessary re-renders" of CountButton
.
I would like to re-iterate that I strongly advise against using React.memo
(or it's friends PureComponent
* and shouldComponentUpdate
) without measuring because those optimizations come with a cost and you need to make sure you know what that cost will be as well as the associated benefit so you can determine whether it will actually be helpful (and not harmful) in your case, and as we observe above it can be tricky to get right all the time, so you may not be reaping any benefits at all anyway.
Browser under-the-hood
When you type "google.com" into your browser, that will happen when you type enter till everything is displayed on your screen?
- DNS lookup (in case you already access google.com before and also in case you do not know the IP of google.com)
- Which protocol DNS use and why?
- The other of place to look up DNS.
- TCP or UDP will be used in this case? why?
- How to know "google.com" require HTTP or HTTPS? how browser can know and redirect from HTTP to HTTPS?
- After you get the HTML content for "google.com" how to get the *.js and image files?
- When getting *.js or image files do why use another TCP connection or use the same one as in the get HTML content? How DNS lookup work in this case?
- After your browser display "google.com" fully, is there any connection open? -> HTTP1.1 Keep-Alive
- Caching can apply to which steps? How caching applied? How to know if the cache is used?
- What is ES5, ES6, ES7, ES8? What is polyfill?
- What is the difference between
var
andlet
? How aboutconst
? - What does "JavaScript is an asynchronous, single-thread language" mean?
- How does browser's Event-Loop work? -> Microtask vs Macrotask
- What is
requestAnimationFrame
? How it works? When to use it? - What is
requestIdleCallback
? How it works? When to use it? - What are the primitive types of JavaScript/TypeScript
- How do you manage the styling? -> BEM, CSS in JS, Styled-Components, etc. What is the pros/cons -> Why not atomic CSS (Tailwind)?
- “Should I use pixels or ems/rems?!”
Details ems/rems
The truth is, if you want to build the most-accessible product possible, you need to use both pixels and ems/rems. It's not an either/or situation. There are circumstances where rems are more accessible, and other circumstances where pixels are more accessible.
To solve this problem, the CSS language designers created the rem unit. It stands for Root EM
.
The rem unit is like the em unit, except it's always a multiple of the font size on the root node, the <html> element
. It ignores any inherited font sizes, and always calculates based on the top-level node.
Documents have a default font size of 16px
, which means that 1rem has a “native” value of 16px.* We can re-define the value of 1rem by changing the font-size on the root node.
Remember earlier, when we said that1rem
was equal to 16px? That's only true if the user hasn't touched their default font size
! If they boost their default font size to 32px, each rem will now be 32px instead of 16.
- How/When does React re-render? Virtual DOM? What is React's reconciliation? -> Two phases commit?
- Class component vs Function component? When to use Class over Function? -> Error boundary is a good example
- Why we need React.memo or React.PureComponent, how about
useCallback
anduseMemo
? - Which are you using for state management? How do you use them? -> Compare Redux vs Mobx vs Redux-saga vs Redux-thunk
- Should I use Context or Redux? Why? -> Context by itself is not a state management system, it’s a dependency injection mechanism
- What is React Lazy Loading? How do you use it? -> React.lazy()
- What do you know about Next.js? Why server-side rendering? Server-side rendering vs client-side rendering?
- What is React 18 Concurrent Mode?
- React Native sounds like a good choice but some companies decided to migrate back to two separate code bases for iOS and Android? What is your take?
- What is the difference between React/Vue/Angular, which one do you like better? -> Re-render mechanism how different?
- Do you write tests? How do you write them? How about E2E tests? Do you use TypeScript?
- In your opinion, when to reach for Redux/state management tools?
- Three principles of Redux:
- Single source of truth: The state is a single source of truth.
- State is read-only: The state cannot be changed from outside the component.
- Changes are made with pure functions: The state is only changed by passing an action to the store.
- What can we do to improve web application performance?
- What is Progressive Web App? What are the benefits of it?
- How you build a frontend app? What is Webpack/Babel? Webpack vs Vite? Code splitting?
- How do you host a frontend app? -> Use a CDN? How about cloud service like Vercel, Netlify?
- How do you debug/benchmark your frontend app?
- How do you monitor your frontend app? -> Use Sentry/logging tool?