Pies Doc
website·Indexed 29 days ago
https://piesdoc.com/Documentation and guides for React and Vue 3, covering core concepts such as hooks (useState, useEffect, useRef), component rendering, reactive values, and Vue 3 essentials including ref(), reactive(), composables, and props.
What's inside Pies Doc
- Pies Doc is a free handbook designed to help developers improve code quality by focusing on the fundamentals of APIs and the theory behind their usage. Rather than analyzing raw source code, it emphasizes correct API usage to help developers identify anti-patterns and reduce bugs caused by inappropriate implementation.
Understand the useEffect hook lifecycle
useEffect()is a built-in React hook used to handle side effects (e.g., state changes that happen indirectly). It takes a callback function and an optional dependency array.Execution flow:
- Mounting: The callback is always called immediately after the component mounts.
- Re-rendering:
- If
dependenciesisundefined, the callback runs on every render. - If
dependenciesis an array, React usesObject.is()to compare each element with the previous render. The callback runs only if at least one element has changed. - If
dependenciesis an empty array[], the callback never runs during re-renders.
- If
- Cleanup: If the callback returns a function, React calls that cleanup function before the next effect execution and immediately before the component unmounts.
useEffect(() => { // Effect logic here }, [dependency1, dependency2]);Prerequisites for using Pies Doc
Pies Doc is designed for developers who have a basic understanding of JavaScript and some foundational knowledge of the subject they are studying. Absolute beginners with zero experience may find the content difficult. To prepare, it is recommended to complete a basic official tutorial, such as building a simple todo or counter app.Understand React Hooks fundamentals
Hooks are functions called at the root level of a function component. They allow you to encapsulate logic and return any value (or nothing) to the component.
Distinctions:
- vs. Utility Functions: A function is considered a 'hook' if it uses React-specific features (e.g.,
useState,useEffect). - vs. Components: Components are named with a capital letter and invoked via JSX (
<Foo />). Hooks are named with auseprefix and invoked as standard function calls. Returning JSX from a hook does not make it a component.
- vs. Utility Functions: A function is considered a 'hook' if it uses React-specific features (e.g.,
Understand state batching in React
Batching is the process where React groups multiple state updates into a single re-render to improve performance and prevent UI flickering.
- React 18+: All updates are batched by default, regardless of where they originate.
- Pre-React 17: Only updates inside React event handlers (e.g.,
onClick,onChange) were automatically batched. - Behavior: If multiple
setStatecalls are made within a single function, React will process all update schedulers first and perform only one render using the final calculated values.
const [name, setName] = useState(''); const [count, setCount] = useState(0); const updateData = () => { setName('A'); setCount(1); setName('B'); setCount(2); setName('C'); setCount(3); // Component re-renders only ONCE with name='C' and count=3 }Learning objectives and benefits of Pies Doc
Pies Doc focuses on the fundamentals of the API and the theory behind it rather than raw source code analysis. By mastering these fundamentals, developers can identify anti-patterns and reduce bugs or inefficient workarounds caused by improper API usage, providing a necessary foundation for comprehending advanced technical knowledge.Understand how reactive values behave during React renders
In React, every render has its own unique set of props and state. Reactive values (like those fromuseState) do not change their value within a single render; they act as constants for the duration of that render. When a state update is triggered, React schedules a re-render. The updated value is only available in the next render cycle. This explains why aconsole.logimmediately following asetCountcall, or inside asetTimeoutclosure from a previous render, will still show the old value: they are referencing the variable binding from the render in which they were created.Understand reactive values and component re-rendering
In React, a reactive value is any value that, when changed, triggers the component to re-render. Re-rendering means React runs the component code from top to bottom to produce new JSX, which is then used to update the DOM during the commit phase.
Reactive values include:
- Values generated by
useState() - Values generated by
useReducer() - Component props
Note on Context: Values from
createContext()are only reactive if the context value and its update function are themselves generated usinguseState()oruseReducer(). If a non-reactive value is placed in context, updating it will not trigger a re-render.- Values generated by
Determine when to make a variable reactive
Follow this rule of thumb when declaring variables in React:
- Make it reactive: If the value will change AND the user must see that change reflected on the screen immediately.
- Keep it non-reactive: If the value changes but does not need to trigger a UI update, or if the value is static.
Extract complex effect logic into custom hooks
To improve component readability and organization, move complexuseEffectlogic and its supporting helper functions into a custom hook. This is recommended even if the hook is only used within a single component, as it removes noise from the main component body and encapsulates the effect's behavior under a descriptive name (e.g.,useSyncUser).import { useSyncUser } from './UseSyncUser' export const Example = (props) => { // The complex effect logic is now encapsulated useSyncUser(props) return ( <div>...</div> ) }Prevent child re-renders using useCallback and memo
When passing a function as a prop to a child component wrapped in
memo(), the child will still re-render if the function is redeclared on every render of the parent (because functions are non-primitive values). To prevent this, wrap the function inuseCallback().To avoid adding state variables to the
useCallbackdependency array (which would trigger the function to be recreated), use an updater function within the state setter (e.g.,setCount(prev => prev + 1)).import { memo, useState, useCallback } from 'react' const MemoizedChild = memo(({ increment }) => { console.log('Child rendered'); return <button onClick={increment}>Increment</button> }) export const Example = () => { const [count, setCount] = useState(0) // useCallback ensures the function reference stays the same // The updater function (prev => prev + 1) removes the need for 'count' in dependencies const increment = useCallback(() => { setCount(prev => prev + 1) }, []) return ( <div> <h1>Count: {count}</h1> <MemoizedChild increment={increment} /> </div> ) }Memoize values and calculations with `useMemo()`
useMemo()is a hook used to memoize a value. It takes a callback function and a dependency array. The callback is executed on the first render, and the result is cached. On subsequent renders, the callback is only re-executed if one or more elements in the dependency array have changed (checked viaObject.is()).Common use cases:
- Expensive Calculations: Avoid running heavy logic (like filtering large arrays) on every render.
- Referential Stability: Ensure non-primitive values (objects, arrays) maintain the same memory address across renders. This is critical when passing such values as props to a component wrapped in
memo(), as a new object reference would trigger a re-render. - Replacing
useEffect+setState: When a value can be derived directly from existing props or state,useMemois cleaner and more performant than updating a separate state variable inside an effect.
import { useState, useMemo } from 'react' export const Example = ({ keyword }: { keyword: string }) => { const [users] = useState([ { id: 1, name: 'User A' }, { id: 2, name: 'User B' }, { id: 3, name: 'User C' }, ]) // 1. Skipping expensive calculations const matchedUsers = useMemo( () => users.filter((user) => user.name.includes(keyword)), [users, keyword] ) // 2. Maintaining referential stability for objects const userConfig = useMemo(() => ({ age: 5, }), []) return ( <div>{/* Render matchedUsers and userConfig */}</div> ) }