React Performance Optimization and JavaScript Iteration Protocols
Render Optimization and State Selection
In React, a parent component re-rendering will recursively trigger re-renders in all of its child components. To prevent unnecessary renders, utilities like React.memo or useMemo should be employed.
When using Redux with React, the connect HOC has largely been replaced by the useSelector and useDispatch hooks. However, precise state selection is critical for performance. Because useSelector uses strict equality (===) by default, returning a new object reference on every call will cause infinite re-renders.
import { createSelector } from '@reduxjs/toolkit';
import { useSelector } from 'react-redux';
// Memoized selector for precise updates
const selectActiveProfile = createSelector(
(state) => state.profileData,
(profile) => profile.displayName
);
const displayName = useSelector(selectActiveProfile);If you must return an object from useSelector, use shallowEqual as the second argument to perform a shallow comparison of the object's properties:
const { displayName } = useSelector((state) => state.profileData, shallowEqual);SSR, Hydration, and Unique Identifiers
The useId hook generates unique identifiers that remain consistent across server and client environments. This prevents the "hydration mismatch" errors that occur when server-rendered HTML differs from the client's initial render. During hydration, React attaches event listeners to the existing server-rendered markup rather than recreating the DOM.
Server-Side Rendering (SSR) is often preferred over Client-Side Rendering (CSR) for improving initial load performance and SEO. In an isomorphic application, a single codebase executes on both the Node.js server and the browser.
Concurrent Rendering
React provides built-in hooks to manage heavy UI updates without blocking the main thread:
const [items, setItems] = useState([]);
const [activeId, setActiveId] = useState('');
const deferredItems = useDeferredValue(items);
const [isPending, startTransition] = useTransition();Custom Iterators and Iterables
An iterator is an object implementing a next() method that returns elements sequentially. When the sequence is exhausted, it returns { done: true, value: undefined }.
class SequenceStepper {
constructor(collection) {
this.collection = collection;
this.cursor = -1;
}
next() {
this.cursor++;
const { collection, cursor } = this;
if (cursor >= collection.length) {
return { done: true, value: undefined };
}
return { done: false, value: collection[cursor] };
}
}An object is considered iterable if it implements the Symbol.iterator method, which must return an iterator. Native iterables include Arrays, Sets, Maps, Strings, and the arguments object. To make a non-iterable object compatible with for...of, you can assign it the array iterator:
myObject[Symbol.iterator] = Array.prototype[Symbol.iterator];Generator Functions
Generators offer a powerful syntax for defining iterables using function* and the yield keyword. Generator objects expose next(), throw(), and return() methods.
function* generateId() {
let current = 1;
yield current++;
}Arguments passed to next() are evaluated as the return value of the currently suspended yield expression. Note that the first next() call cannot accept a value, as there is no paused yield to assign it to.
function* processFlow(...initialArgs) {
const base = 50;
const injected = yield base + 50; // 'injected' receives the argument from the second next() call
yield injected + 50;
}
const runner = processFlow('start');
runner.next(); // { value: 100, done: false }
runner.next(200); // { value: 250, done: false }Generators can be nested using yield*, which delegates iteration to another generator or iterable:
function* innerSeq() {
yield 20;
yield 30;
}
function* outerSeq() {
yield 10;
yield* innerSeq();
yield 40;
}
// Sequence: 10, 20, 30, 40
const skills = ['React', 'TypeScript', 'Node'];
function* iterateSkills() {
yield* skills;
}