Explanation of React Fiber
Introduction
React Fiber is the core reconciliation engine of React supported since version 16, helping to change the rendering mechanism from synchronous to asynchronous.
Advantages:
- Ability to pause and resume work: Allows React to break the rendering process into small units of work (fibers) so as not to block the main thread.
- Task prioritization: Prioritizes direct user interactions (keystrokes, clicks) over background data processing tasks.
- UX Improvement: Minimizes lag (jank) when the application has to handle complex UI components or large data lists.
Detailed Workflow
- React Rendering Pipeline
- Trigger Phase
- When you call setState() or dispatchAction, React marks the changed nodes as dirty and uses the lane mechanism to mark upwards to the corresponding parent Fiber Nodes.
- Fiber: Acts as a "unit of work." Each component is a Fiber node containing information about state, props and links to parent/child/sibling nodes.
- Render Phase - Calculation
- This is where Reconciliation and Fiber demonstrate their greatest roles.
- Scheduling: React's Scheduler considers the priority of the update (High priority like mouse clicks or Low priority like fetching data).
- Fiber Tree Traversal: React starts traversing from the Root to create a new tree called the workInProgress tree.
- Thanks to dirty marking combined with lanes from the Trigger phase, React will Bail Out nodes that are not marked; only marked fiber nodes are checked and rendered for comparison.
- Virtual DOM & Diffing: React runs the render logic of components to create a new Virtual DOM. Then, it compares (Diffing) this new V-DOM tree with the current V-DOM tree to find the list of necessary changes (e.g., needing to add a tag, deleting a tag).
- Characteristics: This phase is entirely asynchronous. React can work a bit, pause to let the browser paint the UI (thanks to the Scheduler using a custom polyfill similar to requestIdleCallback), then return to continue without hanging the browser.
- Commit Phase - Execution
- After having the list of changes ("Effect List"), React moves to the Commit phase.
- Real DOM Update: React directly intervenes in the real DOM (via appendChild, removeChild, setAttribute,...).
- Tree Swapping (Double Buffering): After the update is complete, React simply swaps: the workInProgress tree now becomes the current tree.
- Characteristics: This phase occurs synchronously to ensure users do not see a partial UI state.
- Browser Rendering Pipeline: after React finishes the Commit Phase, React's job is essentially done, but the user hasn't seen anything yet. At this point, the Browser Rendering Pipeline begins to run to draw those changes on the screen:
- Style: Calculate CSS for elements.
- Layout: Determine the size and position of each node on the screen (often called Reflow).
- Paint: Color the pixels (into layers).
- Composite: Combine layers together based on z-index to display the final image on the screen.
Priority
Below are the priority levels from highest to lowest in the scheduler package:
- Immediate Priority (Level 1)
- Behavior: This task will block the main thread until completion.
- Usage: Used for extremely urgent situations needing immediate response so users don't feel the app is "frozen" at the moment of physical interaction.
- User Blocking Priority (Level 2)
- Behavior: Very high priority. If this task is not performed, it will be elevated to "Immediate" level to force browser processing.
- Usage: Usually associated with Discrete Events (click, keystroke, drag and drop). This level helps the UI respond quickly to user intent.
- Normal Priority (Level 3)
- Behavior: This is the default priority for most state updates in React.
- Usage: Work such as data fetching, rendering normal lists will reside here. Fiber can interrupt and rest freely at this level before it becomes urgent.
- Low Priority (Level 4)
- Behavior: Low priority. Only performed when tasks at Normal level are finished or when waiting too long.
- Usage: Analytical tasks (analytics), data logging, or rendering "nice to have" components that don't need to appear immediately.
- Idle Priority (Level 5)
- Expiration time: Never expires (theoretically uses a very large number as the wait time).
- Behavior: Only runs when the browser is truly "completely idle."
- Usage: Memory cleanup tasks, background cache preparation for subsequent screens the user hasn't seen yet.
You can see specifically in the React code here which defines the specific expiration time values for each level. However, these values may be adjusted in future versions if issues arise, so you should only use them for reference purposes.
Detail
Here I will provide examples demonstrating React Fiber's operation in practice.
First, let's create the UseDefault.tsx file to simulate a heavy rendering issue.
This code demonstrates React's default rendering behavior. When a user enters text into the search box, the handleChange function updates filterTerm and performs data filtering immediately. With 10,000 records, processing and updating filteredList happens synchronously on the main thread. As a result, if the table rendering is too heavy, the Input box may stutter and not respond smoothly to the user's typing.
To solve the issue, we will use useTransition and useDeferredValue, both part of React's Concurrent Rendering suite to prioritize UI responsiveness.
- useTransition is a hook that allows you to mark a state update as "non-urgent."
- It is an Action-based control function.
- Use it when you are the one initiating the change (e.g., writing onClick, onChange functions) and have access to the setState function.
- Pending state (isPending): Provides a boolean variable to help developers display loading indicators or professionally blur old content.
- Delaying state changes helps keep the UI responsive: Users can still interact with other parts of the page (like input) while React is calculating the render in the background.
- Avoids UI "freezing": Prevents heavy state updates from stalling the browser.
- useDeferredValue allows you to defer re-rendering non-urgent updates until the browser is idle, keeping the UI responsive during heavy computations.
- It is a Value-based control hook.
- Takes a value and returns a "deferred" copy to help delay re-rendering based on the new value.
- Use it when you receive a prop or a value from another Hook and that value causes lag when rendering, but you do not manage where that value changes.
- Does not have isPending like useTransition; you must compare manually (value !== deferredValue).
Create file UseTransition.tsx
The code uses useTransition to separate two state updates. The setFilterTerm update happens immediately so users see the characters they just typed. Meanwhile, calculating the filter for a 10,000-item array and updating setFilteredList is wrapped in startTransition, marking it as a low-priority task. React will process it in the background and provide the isPending variable to show a Spin icon and a table blur effect, ensuring the UI never "freezes."
Create file UseDeferredValue.tsx
In this code, useDeferredValue is used to receive a delayed "copy" (deferredText) of the text value. When the user types quickly, the text variable is updated immediately so the Input remains smooth, while deferredText (used for heavy list filtering) will have its update deferred until the browser is idle. The isStale variable helps users recognize the UI is in an old data state by blurring the list, creating a more professional experience.
Happy coding!
Comments
Post a Comment