State Management
Relevant source files
The following files were used as context for generating this wiki page:
The QR-Tool is a client-side application built with React. Its state management is self-contained within the browser, primarily using React hooks (useState, useEffect, useMemo) and the browser's localStorage API for persistence. There is no backend server or complex state management library involved; all logic resides within the App component and its children.
State is categorized into three main types: data state (the content of the QR code), UI configuration state (visual aspects like color and size), and transient UI state (like scan results or notifications). User preferences for UI configuration are persisted in localStorage to enhance user experience across sessions.
Core Architecture
The application's state is centralized within the main App.jsx component. This component holds all the primary state variables and passes them down to child components as props. This follows a unidirectional data flow pattern.
State Categories
The state can be broken down as follows:
- QR Data State (
qrData): An object containing the input values for all supported QR code types. - QR Type State (
qrType): A string that determines the current type of QR code being generated (e.g., 'url', 'wifi'). - UI Configuration State: A collection of state variables for QR code appearance (e.g.,
size,fgColor,bgColor,eccMode,logoMode). - Transient State: Temporary state for UI feedback, such as
validationErrors,scanResult, and toast notifications. - Derived State: Values computed from other state variables using
React.useMemofor performance, such as the finalqrContentstring and theqrPropsobject passed to the rendering component.
This diagram illustrates the flow from user interaction to the final rendered QR code.
Sources: src/App.jsx:291-344, 437-466
State Persistence
User settings are persisted across sessions using the browser's localStorage. This is managed by a custom hook, useLocalStorage, which is defined directly within App.jsx.
useLocalStorage Hook
This hook is a wrapper around React.useState. It initializes its state by attempting to read and parse a value from localStorage. If no value is found, it falls back to a provided initial value. A React.useEffect hook then ensures that any change to the state is automatically written back to localStorage.
The hook includes error handling with a try...catch block to prevent crashes if localStorage is disabled or full. It also features a disabled flag (privateMode in the app) to completely bypass localStorage interaction.
Sources: src/App.jsx:258-276, README.md:21
// src/App.jsx:258-276
function useLocalStorage(key, initialValue, disabled = false) {
const [val, setVal] = React.useState(() => {
if (disabled) return initialValue;
try {
const v = localStorage.getItem(key);
return v !== null ? JSON.parse(v) : initialValue;
} catch {
return initialValue;
}
});
React.useEffect(() => {
if (disabled) return;
try {
localStorage.setItem(key, JSON.stringify(val));
} catch (error) {
console.warn('Failed to save to localStorage:', error);
}
}, [key, val, disabled]);
return [val, setVal];
}
The following sequence diagram shows the interaction flow for the useLocalStorage hook.
Sources: src/App.jsx:258-276
Persisted State Variables
The following table lists the key state variables that are persisted in localStorage. The privateMode flag, hardcoded to true, currently disables this persistence.
| State Variable | localStorage Key | Initial Value | Description |
|---|---|---|---|
qrType | qr_type | 'url' | The selected type of QR code. |
uiMode | ui_mode | 'quick' | The UI mode (quick or pro). |
size | qr_size | 360 | The pixel size of the QR code. |
selectedPreset | qr_size_preset | 'web' | The selected size preset ID. |
eccMode | qr_ecc_mode | 'AUTO' | The Error Correction Level mode. |
includeMargin | qr_margin | true | Whether to include the quiet zone. |
transparentBg | qr_transparent_bg | false | Whether the PNG background is transparent. |
fgColor | qr_fg | '#000000' | The foreground (QR) color. |
bgColor | qr_bg | '#FFFFFF' | The background color. |
format | qr_fmt | 'canvas' | The export format (canvas or svg). |
logoMode | qr_logo_mode | 'auto' | The logo scaling mode. |
customLogoScale | qr_logo_custom | 0.2 | The custom scale for the logo. |
Sources: src/App.jsx:282-321, SECURITY_AUDIT_REPORT.md:89-95
Application State (App.jsx)
The App.jsx component is the single source of truth for the application's state.
QR Data and Validation
The qrData state is a large object that holds all possible input fields for every QR code type. A single updateQrData function is used to update its properties.
// src/App.jsx:283-305
const [qrData, setQrData] = React.useState({
url: 'https://example.com',
text: '',
ssid: '',
password: '',
encryption: 'WPA',
hidden: false,
name: '',
firstName: '',
lastName: '',
phone: '',
email: '',
org: '',
vcardUrl: '',
subject: '',
body: '',
message: '',
lat: '',
lng: '',
label: '',
});
const updateQrData = (k, v) => setQrData((prev) => ({ ...prev, [k]: v }));
Input validation runs within a useEffect hook that triggers whenever qrType or qrData changes. It populates a validationErrors state object, which is then used to display error messages to the user and apply error styling to input fields.
Sources: src/App.jsx:309-332
Derived and Debounced State
To optimize performance and separate concerns, several key values are derived from the primary state using React.useMemo.
qrContent: The raw string to be encoded in the QR code. It is generated bygenerateQRContentbased onqrTypeandqrData.text: A debounced version ofqrContentusing theuseDebouncedhook. This prevents the QR code from re-rendering on every keystroke, improving performance.contrastRatio: Calculated fromfgColorandbgColorto provide warnings about low-contrast color combinations.level: The final ECC level, automatically determined bypickECCbased on user selection, logo size, and color contrast.qrProps: A memoized object containing all the props required by theqrcode.reactcomponent. This is a critical optimization, as it prevents the expensive QR code rendering process from running unnecessarily.
The diagram below shows how qrProps is assembled from various state sources.
Sources: src/App.jsx:437-466
Local Component State
While most state is centralized in App.jsx, some components manage their own local, transient state.
A clear example is the ExportPanel.jsx component, which uses a local isExporting state variable to manage the disabled state of its download buttons during the file export process. This state is entirely self-contained and does not need to be shared with the rest of the application.
// src/components/ExportPanel.jsx:13
const [isExporting, setIsExporting] = React.useState(false);
Sources: src/components/ExportPanel.jsx:13
Summary
The state management in QR-Tool is intentionally simple and robust for a client-side application. It leverages standard React hooks for managing component state and a custom hook for persisting UI preferences to localStorage. By centralizing the application state in the App component and using memoization for derived data, the architecture remains performant and easy to maintain. The unidirectional data flow ensures predictability and simplifies debugging.