Advanced Web Technologies

Chapter 4
Front-End Frameworks and State Management

Bachelors Program in Computer Applications
Advanced Web Technologies   •   Bachelor

Front-End Frameworks and State Management

Introduction

Modern web applications increasingly behave like desktop applications: they update portions of a page without full reloads, validate input immediately, preserve navigation history, communicate with APIs, and adapt to multiple devices. Building such applications directly with browser APIs is possible, but becomes difficult as the number of views, interactions, and data dependencies grows. Front-end frameworks address this complexity through component models, declarative rendering, routing, lifecycle management, and structured state management.

This chapter uses contemporary framework concepts, with React as a principal example, while comparing React, Angular, and Vue. The aim is not to teach one framework’s syntax alone, but to develop transferable design and engineering skills. A well-designed single-page application (SPA) should be maintainable, accessible, testable, secure, and capable of performing well on both fast and constrained networks.

Learning Objectives

After completing this chapter, a student should be able to:

  1. Explain component-based architecture and declarative user-interface development.
  2. Build a small SPA using components, routing, forms, and asynchronous API calls.
  3. Distinguish local, shared, server, and URL state.
  4. Apply lifecycle concepts without introducing memory leaks or stale data.
  5. Select suitable state-management techniques for different application sizes.
  6. Design reusable components and coherent design systems.
  7. Explain server-side rendering, static generation, hydration, and their trade-offs.
  8. Compare React, Angular, and Vue using technical and organizational criteria.
  9. Evaluate accessibility, security, performance, and maintainability in front-end systems.

1. The Component-Based Model

A component is an independently understandable unit of user-interface structure, behavior, and presentation. A component may receive input properties, maintain internal state, respond to events, and render child components.

A useful conceptual model is:

UI = Render(State, Properties)

This is a design principle rather than a universal executable formula. It means that the visible interface should be determined predictably by current data and configuration. When state changes, the framework calculates the required update rather than requiring the developer to manually modify every affected DOM node.

Imperative and declarative development

In imperative DOM programming, a developer specifies steps such as “find this element, change its text, add a class, and hide another element.” In declarative programming, the developer describes the desired interface for a given state. The framework reconciles the description with the browser DOM.

Declarative development generally improves consistency, but it does not eliminate complexity. Developers must still design state transitions, handle asynchronous failures, control effects, and understand rendering performance.

Component composition

Large interfaces should be composed from small units. For example, an online bookstore may contain App, Header, SearchBox, BookList, BookCard, CartSummary, and CheckoutForm. Composition is preferable to a single “god component” because each part can be tested, reused, and changed independently.

A component should normally have:

  • a clear responsibility;
  • a narrow and meaningful interface;
  • predictable inputs and outputs;
  • minimal hidden coupling;
  • accessible semantic markup.

Properties should be treated as inputs supplied by a parent. Local state should be used only for data whose ownership belongs to that component. If several distant components need the same data, the data may need to be lifted to a common owner or placed in a shared store.

2. Single-Page Applications and Routing

An SPA loads an application shell and subsequently updates views through client-side JavaScript. Navigation may change the URL and displayed content without downloading a complete document for every route. A router maps URL patterns to components, for example:

URL patternViewTypical purpose
/HomeLanding page
/productsProduct listBrowse resources
/products/:idProduct detailDisplay one resource
/accountAccountAuthenticated user area
/not-foundError viewUnknown route

Routing must address more than visual switching. It should support browser history, direct links, route parameters, protected routes, pending states, and meaningful document titles. A protected route should not be treated as a security boundary: authorization must also be enforced on the server.

Client-side and server-side routing

With client-side routing, the browser receives an application shell and the router interprets the path. The web server must be configured to return the application entry document for valid application paths; otherwise, refreshing /products/42 may produce a server-level 404.

Server-side routing, by contrast, generates a response for each request. Hybrid frameworks combine both approaches. The appropriate choice depends on SEO requirements, interaction complexity, caching, deployment architecture, and initial-load performance.

3. State: Categories, Ownership, and Flow

State is data that can influence behavior or rendered output. Treating all state as one category leads to poor architecture. Four useful categories are:

State categoryExamplesAppropriate location
Local UI stateModal visibility, input focus, selected tabComponent or nearby parent
Shared client stateTheme, shopping cart, authenticated userContext or state store
Server stateProducts, invoices, loading and error statusQuery/cache layer
URL stateSearch term, page number, filterRouter and URL

State ownership

The component that owns state should be the smallest component that needs to coordinate all relevant consumers. If a parent and two children need a selected product, the parent may own the selection and pass it down. If unrelated screens need the shopping cart, a shared store is more appropriate.

State should have one authoritative source whenever possible. Duplicating the same value in multiple places creates synchronization problems. For example, storing both cartItems and an independently edited cartTotal can produce inconsistency. The total should usually be derived from cart items.

Events and unidirectional data flow

A common architecture is:

User action → event handler → state transition → render → updated interface

Parents pass data downward; children communicate upward through callbacks or dispatched actions. This unidirectional flow makes changes easier to trace than unrestricted two-way mutation.

A reducer expresses state transitions as a function of current state and an action:

function cartReducer(state, action) {
  switch (action.type) {
    case "addItem":
      return { ...state, items: [...state.items, action.item] };
    case "removeItem":
      return {
        ...state,
        items: state.items.filter(item => item.id !== action.id)
      };
    default:
      return state;
  }
}

The reducer should be deterministic and free of network requests, timers, and direct mutation. Side effects belong in event handlers, effect mechanisms, or dedicated data-fetching libraries.

State-management choices

A practical progression is:

  1. Local component state for isolated interaction.
  2. Lifted state for closely related components.
  3. Context or dependency injection for stable cross-cutting values.
  4. A centralized store for complex shared client state.
  5. A server-state library for caching, refetching, invalidation, and request deduplication.

A centralized store is not automatically superior. It introduces conventions, debugging tools, and an additional dependency, but also increases conceptual overhead. Server state should not be copied into a client store merely because it is data. Server data has freshness, caching, retry, and invalidation concerns that ordinary UI state does not.

4. Lifecycle and Effects

A component commonly passes through mounting, updating, and unmounting phases. Framework terminology differs, but the underlying concerns are similar:

  • initialize state;
  • render from current inputs;
  • synchronize with external systems;
  • respond to changes;
  • release subscriptions and resources.

An effect is appropriate when a component must synchronize with something outside the rendering calculation, such as a WebSocket, browser event listener, timer, or external library. It is not normally required merely to calculate a value from existing state; such values should be derived during rendering or memoized when evidence supports memoization.

A safe subscription pattern includes cleanup:

useEffect(() => {
  const connection = createConnection(roomId);
  connection.connect();

  return () => connection.disconnect();
}, [roomId]);

The dependency list communicates which values affect the synchronization. Omitting a relevant dependency can produce stale closures; adding unstable values can cause unnecessary repetition. Cleanup is essential because unremoved listeners, timers, and connections can cause memory leaks or duplicate behavior.

Asynchronous effects also need cancellation or result validation. If a user quickly changes search terms, an earlier response may arrive after a later response and overwrite correct data. An AbortController, request identifier, or framework-provided query mechanism can prevent this race.

5. Forms and Validation

Forms are a major source of state and business rules. In a controlled form, input values are represented in application state and updated through event handlers. In an uncontrolled form, the DOM retains the current value and the application reads it when needed. Controlled forms offer explicit synchronization and immediate validation, while uncontrolled forms can be simpler and may reduce rerendering.

A robust form separates:

  • field value;
  • touched or visited status;
  • validation message;
  • submission status;
  • server-side validation errors.

Client validation improves usability but is not a security control. All important validation must be repeated on the server. Validation should be specific, understandable, and associated with the relevant field through accessible labels and error descriptions.

Example conceptual validation:

function validate(values) {
  const errors = {};
  if (!values.email.includes("@")) {
    errors.email = "Enter a valid email address.";
  }
  if (values.password.length < 8) {
    errors.password = "Password must contain at least 8 characters.";
  }
  return errors;
}

The example is intentionally incomplete: production validation should use appropriate email rules, server policies, localization, and password guidance. Password requirements should not encourage insecure composition rules without a security rationale.

6. Reusable Design Systems

A design system combines visual tokens, components, interaction rules, accessibility guidance, and documentation. It is more than a CSS folder. Typical tokens include color roles, spacing, typography, borders, elevation, motion, and responsive breakpoints.

Reusable components should expose meaningful variants rather than allowing every consumer to override arbitrary internal styles. For example, a Button may support primary, secondary, and danger variants, plus disabled and loading states.

Good component APIs:

  • use semantic names;
  • preserve native keyboard behavior;
  • expose accessible names and descriptions;
  • separate content from layout where possible;
  • document states and failure conditions;
  • avoid excessive boolean properties that create confusing combinations.

Accessibility should be designed from the beginning. Use semantic HTML, visible focus indicators, sufficient contrast, keyboard navigation, labels for controls, and appropriate announcements for dynamic status messages. ARIA should supplement semantic HTML, not replace it unnecessarily.

7. Server Rendering, Static Generation, and Hydration

Client-only rendering sends a relatively small HTML shell and creates most content in the browser. Server-side rendering (SSR) produces HTML on the server for each request, after which client JavaScript hydrates the document by attaching event handling and framework behavior.

Static site generation (SSG) produces HTML ahead of time, usually during a build. Incremental or revalidated generation updates selected pages periodically or after an event. These approaches can be combined in one application.

ApproachStrengthsLimitations
Client renderingRich interaction; simple static hostingSlower first meaningful content on weak devices; SEO concerns for some content
SSRFast initial HTML; useful for SEO and personalized responsesServer cost; hydration complexity
SSGExcellent caching and reliabilityContent may become stale; build or revalidation complexity
HybridRoute-specific optimizationMore architectural complexity

Hydration assumes that the initial client render is compatible with the server-generated markup. Nondeterministic values such as current time, random identifiers, browser-only APIs, or differing locale formats can cause hydration mismatches. Browser-dependent logic should be isolated until the client is available, and stable identifiers should be used.

SSR is not automatically faster. Server computation, network distance, JavaScript download, hydration work, and data access all influence real performance. Performance should be assessed using measurements such as Largest Contentful Paint, Interaction to Next Paint, and Cumulative Layout Shift, while recognizing that laboratory and field data answer different questions.

8. Comparing Contemporary Frameworks

React is primarily a UI library with a large ecosystem. It offers flexible composition and multiple choices for routing, data fetching, and state management. This flexibility supports many architectures but can create inconsistency without team conventions.

Angular is a more integrated framework. It commonly includes routing, dependency injection, forms, testing conventions, and a structured project model. It can suit large teams that value prescribed patterns, although its learning surface is substantial.

Vue provides a progressive framework with approachable templates, reactive state, and official solutions for common needs. It often offers a balance between convention and flexibility.

CriterionReactAngularVue
Primary characterUI library/ecosystemIntegrated frameworkProgressive framework
FlexibilityHighMore convention-drivenModerate to high
Learning concernEcosystem choicesBroad framework surfaceTemplate and reactivity concepts
Typical strengthComposition and ecosystemEnterprise structureProgressive adoption
Main trade-offArchitectural variationComplexity and bundle decisionsSmaller ecosystem in some niches

The choice should consider existing expertise, accessibility and testing requirements, hiring, deployment model, application size, ecosystem maturity, long-term maintenance, and framework governance. Benchmark claims should be verified against the application’s real workload rather than generalized from synthetic examples.

9. Applied Case: Campus Course Registration Portal

The following is an illustrative case.

Background

A university wants a responsive portal through which students can search courses, inspect prerequisites, register, and view their timetable. The existing server-rendered system reloads entire pages and loses search filters during errors.

Problem

The portal must support authenticated routes, fast search, validation of prerequisites, conflict messages, accessible forms, and reliable updates when registration capacity changes. Students may open the portal on mobile devices and unstable networks.

Analysis

The team classifies state as follows:

  • search text and page number: URL state;
  • open filter panel: local UI state;
  • current student identity: shared authenticated state;
  • course results and availability: server state;
  • selected courses before submission: local or shared client state;
  • registration result: server response, not merely client state.

This classification prevents the team from placing all data in one global store. Search URLs can be bookmarked, server results can be cached and invalidated, and form drafts can remain local until submission.

Approach and solution

The application uses route-level code splitting so that registration code is not downloaded on the public course-search page. A reusable CourseCard exposes title, credits, prerequisite status, and an accessible registration action. A query layer manages loading, error, retry, caching, and invalidation. After successful registration, the timetable and course-availability queries are invalidated so that stale capacity is not displayed.

The registration form performs immediate client checks for missing selections, then submits to a server endpoint. The server rechecks authentication, prerequisites, timetable conflicts, and capacity in one authoritative transaction. If capacity has changed, the interface presents a clear server error rather than assuming that client state is correct.

A route guard improves user experience by redirecting unauthenticated users to sign in, but server authorization remains mandatory. Automated tests cover reducer transitions, form validation, route behavior, keyboard navigation, and API failure states. Field monitoring records real-user performance and error rates without collecting unnecessary personal information.

Outcome

The resulting system provides bookmarkable searches, clearer failure recovery, smaller initial downloads, and fewer stale-registration messages. The most important improvement is architectural: each state type has an appropriate owner and consistency rule.

Learning points

  1. State classification should precede tool selection.
  2. Client checks improve interaction but cannot enforce business rules.
  3. Cache invalidation is part of correctness, not merely optimization.
  4. Accessibility and error handling must be tested as functional requirements.
  5. Route-level architecture affects both performance and maintainability.

10. Security, Governance, and Professional Practice

Front-end code is untrusted because users can inspect and modify it. Never place secrets, private API keys, or authorization decisions in browser code. Protect against cross-site scripting by using framework escaping, avoiding unsafe HTML injection, sanitizing unavoidable rich content, and applying suitable content-security policies.

Authentication design should consider secure cookies, session expiration, token exposure, cross-site request forgery protections, and logout behavior. The exact mechanism depends on the backend architecture; storing sensitive long-lived tokens in easily accessible browser storage can increase exposure to script-based attacks.

Privacy principles include data minimization, purpose limitation, retention control, consent where required, and protection of telemetry. Analytics and error monitoring should avoid unnecessary personal data. Third-party dependencies require licensing, vulnerability, and supply-chain review.

AI-assisted coding can generate components, tests, and refactoring suggestions, but generated code must be reviewed for accessibility, security, license implications, race conditions, and framework-version accuracy. Automated generation does not transfer professional accountability.

11. Best Practices and Common Failure Modes

Effective practices include:

  • keep rendering pure and effects focused on external synchronization;
  • model loading, success, empty, and error states explicitly;
  • derive values instead of duplicating them;
  • colocate state with its natural owner;
  • use stable keys for lists;
  • validate on both client and server;
  • test behavior rather than implementation details;
  • measure performance before optimizing;
  • use semantic HTML and keyboard testing;
  • document component contracts and architectural decisions.

Common failures include an oversized global store, effects used for ordinary calculations, missing cleanup, stale asynchronous responses, route guards mistaken for authorization, uncontrolled design-system overrides, and hydration mismatches caused by browser-only values. These are not merely style problems; they can produce security defects, inconsistent data, and inaccessible interfaces.

Chapter Summary

Component-based frameworks organize interfaces into composable units whose output is derived from properties and state. SPAs use client-side routing to update views efficiently, but require correct server fallback behavior and secure server authorization. State should be classified as local UI, shared client, server, or URL state, with ownership assigned to the smallest suitable scope. Reducers make complex transitions explicit, while effects synchronize with external systems and require correct dependencies and cleanup.

Forms require clear validation, submission, and error states; client validation complements but never replaces server validation. Design systems create consistent, accessible, reusable components. Client rendering, SSR, SSG, and hybrid approaches involve different performance and operational trade-offs, and hydration requires compatible server and client output. React, Angular, and Vue differ mainly in flexibility, integration, conventions, and ecosystem choices. Professional front-end development also includes accessibility, security, privacy, testing, dependency governance, and evidence-based performance measurement.

Key Terms

Component: A reusable unit of interface structure, behavior, and presentation.

Declarative UI: An approach that describes the desired interface for a given state.

Single-page application: A web application that updates views without full document reloads.

Router: A mechanism that maps URLs to application views or handlers.

Property: Input data passed from a parent component to a child.

Local state: State owned by a component or a small component subtree.

Shared state: Data used by multiple distant components.

Server state: Data originating from and governed by a backend service.

URL state: Data encoded in the address, such as filters or page numbers.

Reducer: A deterministic function that calculates next state from current state and an action.

Side effect: An operation that interacts with systems outside pure rendering.

Controlled form: A form whose values are represented in application state.

Design system: A governed collection of design tokens, components, patterns, and guidance.

Server-side rendering: Generating HTML on a server for a request.

Static site generation: Generating pages ahead of a request, usually during a build.

Hydration: Attaching client behavior to server-generated HTML.

Code splitting: Dividing application code into separately loaded bundles.

Cache invalidation: Marking stored data as stale so that it can be refreshed.

Cross-site scripting: Injection of executable content into a user’s browser context.

Unidirectional data flow: A design in which data moves through predictable parent-to-child and event-driven transitions.

Review Questions

5 Short Answer Questions

  1. What distinguishes declarative UI development from imperative DOM manipulation?
  2. Name four categories of front-end state and give one example of each.
  3. Why is cleanup required for subscriptions and timers?
  4. What is hydration?
  5. Why can a client-side route guard not replace server authorization?

5 Long Answer Questions

  1. Explain component composition, state ownership, and unidirectional data flow in an SPA.
  2. Compare client rendering, SSR, SSG, and hybrid rendering, including their principal trade-offs.
  3. Describe how a complex form should manage values, validation, submission, and server errors.
  4. Compare React, Angular, and Vue using technical and organizational criteria.
  5. Explain how accessibility, security, privacy, and testing should influence front-end architecture.

3 Analytical / Application Questions

  1. A product dashboard stores filters, fetched products, modal visibility, and user preferences in one global store. Classify each item and propose a more suitable ownership strategy.
  2. A search interface sometimes displays results for an earlier query after the user has typed a newer query. Analyze the cause and design a correction.
  3. Design an architecture for a public university course catalogue that must support SEO, authenticated registration, mobile performance, accessibility, and frequently changing course availability.

Multiple Choice Questions with Answers

Question 1

Which statement best describes declarative UI development?

A. It specifies the desired interface for the current state.

B. It requires manually updating every affected DOM node.

C. It eliminates the need for event handlers.

D. It stores all application data in the URL.

Correct Answer: A. It specifies the desired interface for the current state.

Question 2

Which type of state is most appropriate for a search filter that should be bookmarkable?

A. Local component state only

B. URL state

C. A browser timer

D. A CSS custom property

Correct Answer: B. URL state

Question 3

What is the principal responsibility of a reducer?

A. Perform network requests

B. Create database indexes

C. Calculate next state from current state and an action

D. Render server-side HTML only

Correct Answer: C. Calculate next state from current state and an action

Question 4

Why should an effect that creates a WebSocket usually return cleanup logic?

A. To change the document title automatically

B. To prevent duplicate connections and resource leaks

C. To convert server state into URL state

D. To bypass authentication

Correct Answer: B. To prevent duplicate connections and resource leaks

Question 5

Which statement about client-side validation is correct?

A. It makes server validation unnecessary.

B. It is primarily a usability aid and must be complemented by server validation.

C. It guarantees that submitted data is trustworthy.

D. It prevents all cross-site scripting attacks.

Correct Answer: B. It is primarily a usability aid and must be complemented by server validation.

Question 6

What does hydration do in a server-rendered application?

A. Deletes the server-generated HTML

B. Attaches client-side behavior to existing HTML

C. Converts CSS into database records

D. Encrypts all route parameters

Correct Answer: B. Attaches client-side behavior to existing HTML

Question 7

Which framework is generally characterized as an integrated framework with built-in conventions for areas such as routing and dependency injection?

A. Angular

B. React only

C. A CSS preprocessor

D. A database engine

Correct Answer: A. Angular

Question 8

A course-registration request fails because another student took the final seat. Where must the authoritative capacity check occur?

A. Only in the button component

B. Only in client-side form validation

C. On the server during the registration operation

D. Only in the browser’s URL

Correct Answer: C. On the server during the registration operation

Question 9

Which practice most directly reduces hydration mismatch risk?

A. Using random values during both renders

B. Rendering browser-only nondeterministic values unconditionally on the server

C. Ensuring compatible server and client output

D. Removing all semantic HTML

Correct Answer: C. Ensuring compatible server and client output

Question 10

What is a key reason to avoid duplicating a cart total separately from cart items?

A. It prevents CSS inheritance.

B. The two values can become inconsistent.

C. It disables browser history.

D. It makes semantic labels impossible.

Correct Answer: B. The two values can become inconsistent.

Advanced Web Technologies
Chapter 4 of 15