Steps Documentation
website·Indexed 32 days ago
https://steps.shaiwang.life/A collection of technical guides and deep dives covering JavaScript, TypeScript, React, CSS, and general software design patterns. Topics include React rendering and hooks, JS lexical scope and prototypes, TypeScript path aliases, and CSS visibility properties.
What's inside Steps
- In React, "rendering" refers to the process where React calls your components to determine what should be displayed on the screen. For the application to behave predictably, rendering must be a pure calculation, meaning it must satisfy two conditions: it must produce the same output given the same inputs, and it must not modify any state or external variables (it "minds its own business"). To help developers identify impure functions, React's "Strict Mode" intentionally calls component functions twice during development.
Understand the difference between controlled and uncontrolled components in React
In React, a component is considered 'uncontrolled' when it manages its own important information via local state. A component is 'controlled' when its behavior and data are driven by props passed from a parent component.
- Uncontrolled Components: Easier to implement within parents due to less configuration, but less flexible for coordination between multiple components.
- Controlled Components: Provide maximum flexibility as the parent fully specifies behavior, but require more configuration via props.
In practice, most components use a mix of both local state and props. When designing a component, decide which data should be managed internally (uncontrolled) and which should be driven by the parent (controlled), keeping in mind that these can be refactored later.
Understand and use the React event object
React event handlers receive a 'synthetic event' object. This object conforms to the standard DOM event interface but provides a cross-browser wrapper to ensure consistent behavior across different environments. If the original browser event is required, it can be accessed via thenativeEventproperty of the React event object.<button onClick={(e) => { console.log(e); // React event object console.log(e.nativeEvent); // Underlying browser event }} />Understand Lexical Scope in JavaScript
Lexical scope defines the scope of a variable or function based on its physical location within the source code. Because the scope is determined at author-time (where the variable is defined), it remains constant throughout the execution of the program, regardless of where the function is called from.const a = 2 function foo() { console.log(a) } function bar() { const a = 3 foo() // Still logs 2, because foo's scope is defined where it was created, not where it is called } foo() // 2 bar() // 2Measure calculation performance to determine if memoization is needed
To determine if a piece of code is expensive enough to justify memoization, measure the time spent executing that specific calculation. If the logged time is significant (e.g., 1ms or more), memoization may be beneficial.
For accurate results:
- Use a production build of the application.
- Test on hardware similar to your end-users.
- Use CPU Throttling (available in Chrome DevTools) to simulate slower devices, as development machines are typically faster than user devices.
console.time('filter array') const visibleTodos = getFilteredTodos(todos, filter) console.timeEnd('filter array')Prevent component re-renders with React.memo
React.memo is a higher-order component used to wrap function components. It prevents the component from re-rendering unless its props change. For class components, similar behavior is achieved usingPureComponentorshouldComponentUpdate.Perform a shallow copy of an object or array
A shallow copy duplicates only the top-level structure of an object or array. Nested objects or arrays are still passed by reference, meaning modifications to nested data in the copy will affect the original object. Shallow copies are more memory-efficient and faster than deep copies but are risky for nested data structures. Common methods for shallow copying includeObject.assign(),Array.prototype.slice(),Array.prototype.concat(), and the spread operator (...).// Using spread operator for shallow copy const original = { a: 1, b: { c: 2 } }; const copy = { ...original }; // Using Object.assign for shallow copy const copy2 = Object.assign({}, original);Perform a deep copy of an object or array
A deep copy creates a completely independent duplicate of the original object or array, including all nested structures. Modifications to a deep copy do not affect the original object. This process is more computationally expensive and consumes more memory than shallow copying. A common way to achieve a deep copy is usingJSON.stringify()andJSON.parse().const original = { a: 1, b: { c: 2 } }; const deepCopy = JSON.parse(JSON.stringify(original));Intercept child events using React Capture phase handlers
In React, you can catch events on child elements regardless of whether they call
stopPropagation()by using a capture phase handler. This is achieved by appendingCaptureto the end of the standard event name (e.g.,onClickbecomesonClickCapture).Event propagation occurs in three distinct phases:
- Capture Phase: The event travels down from the root, triggering all
onClickCapturehandlers. - Target Phase: The event runs the handler on the actual clicked element.
- Bubble Phase: The event travels back up the DOM tree, triggering all standard
onClickhandlers.
Capture events are primarily useful for infrastructure-level code such as analytics logging or routing logic where you must ensure an event is recorded regardless of the child component's internal propagation logic.
<div onClickCapture={() => { /* This runs first, even if children call e.stopPropagation() */ console.log('Capture phase: event intercepted'); }} > <button onClick={(e) => e.stopPropagation()}>Click Me</button> <button onClick={(e) => e.stopPropagation()}>Click Me Too</button> </div>- Capture Phase: The event travels down from the root, triggering all
Design custom React Hooks for high-level use cases
When creating custom React Hooks, focus on concrete, high-level use cases rather than technical implementation details. A well-named hook should clearly communicate its purpose, inputs, and outputs to other developers. If a hook is difficult to name, it may be too tightly coupled to a specific component's logic and should not be extracted yet.
Recommended naming patterns include:
- Use-case driven:
useData(url),useImpressionLog(eventName, extraData), oruseChatRoom(options). - System-specific (using appropriate jargon):
useMediaQuery(query),useSocket(url), oruseIntersectionObserver(ref, options).
Avoid creating 'lifecycle' hooks that act as convenience wrappers for
useEffect, as they do not align with the React paradigm. Examples of patterns to avoid includeuseMount(fn),useEffectOnce(fn), anduseUpdateEffect(fn).- Use-case driven:
Iterate over async and sync iterables using for await...of
Thefor await...ofstatement creates a loop that iterates over async iterable objects as well as synchronous iterables. Unlike a standardfor...ofloop,for await...ofwill automatically await any promises yielded by the iterable before executing the loop body, ensuring that the loop variable contains the resolved value rather than the Promise object itself.function* generator() { yield 1 yield Promise.resolve(2) yield 3 } (async () => { // for await...of resolves promises automatically for await (const num of generator()) { console.log(num) } })() // Output: 1, 2, 3 // Comparison: standard for-of does NOT resolve promises for (const numOrPromise of generator()) { console.log(numOrPromise) } // Output: 1, Promise { 2 }, 3Manage npm dependencies in frontend projects with build tools
In frontend projects where code is compiled by build tools (such as Webpack) before being served to the browser, there are no runtime dependencies in the traditional Node.js sense. Because the build process bundles all necessary code into the final output, all packages—including those used by the application at runtime—can be listed underdevDependencies. This pattern is commonly used by major frameworks like Vue.js and React.