Technical Blog Article — App 33: Building a Contact Agenda with React, TypeScript, Fluent UI, and Controlled Components

Modern React applications are fundamentally built around one central idea:

The UI is a function of state.

This idea may sound simple initially, but it completely changes how frontend applications are designed. Instead of manually manipulating HTML elements, React applications describe the interface declaratively based on the current application state.

npm run build

  • dynamic state management
  • controlled forms
  • derived state
  • array updates
  • reusable component composition
  • enterprise UI organization

npm run build

The app introduces a real-world enterprise scenario:
a small corporate contact management system.

Even though the application is relatively small, architecturally it introduces many professional React patterns that are used in:

  • CRM systems
  • internal portals
  • Microsoft-style dashboards
  • SharePoint solutions
  • admin systems
  • user management interfaces

The app was built using:

  • React
  • TypeScript
  • Vite
  • Fluent UI
  • controlled inputs
  • reusable components
  • declarative rendering

This app is especially important because it introduces the complete React data flow:

User Input
Event Handler
State Update
React Re-Render
Updated UI

This flow is the heart of React itself.


Project Goal

The objective of App 33 is to create an enterprise-style contact agenda with:

  • a contact registration form
  • a search filter
  • reusable contact cards
  • controlled inputs
  • dynamic rendering
  • Fluent UI styling
  • TypeScript models

Unlike earlier static UI apps, this application already behaves like a real business application because the UI changes dynamically according to user interaction.

This is the transition point between:

  • static UI rendering
    and
  • real reactive applications

React Learn Concepts Used

This app directly connects to several core sections from the official React documentation:

React State

Controlled Inputs

Updating Arrays

State Structure

These concepts are foundational for all modern React development.


Creating the Project

The application starts with Vite.

Why Vite?

Vite provides:

  • extremely fast startup
  • modern ES module support
  • instant Hot Module Replacement
  • optimized production builds
  • simplified React configuration

Project creation:

cd C:\ReactApps
mkdir bloco02
cd bloco02
npm create vite@latest app33-contact-agenda -- --template react-ts

The template:

react-ts

automatically configures:

  • React
  • TypeScript
  • Vite
  • JSX compilation
  • development scripts

Installing Dependencies

After project creation:

cd app33-contact-agenda
npm install

Install Fluent UI:

npm install @fluentui/react-components @fluentui/react-icons

Creating the Folder Structure

mkdir src\components
mkdir src\models
mkdir src\data
mkdir src\styles

This structure is extremely important.

Modern React applications scale through separation of responsibility.


Understanding the Folder Structure

components/

Contains reusable UI components.

Examples:

  • ContactCard
  • ContactList
  • SearchBar
  • ContactForm

React applications scale by composing components together.


models/

Contains TypeScript contracts and interfaces.

This helps define:

  • object shapes
  • API contracts
  • application entities

data/

Contains static or mock data.

Later this folder may evolve into:

  • API services
  • repositories
  • fetch layers

styles/

Prepared for:

  • reusable CSS
  • layouts
  • theme customizations

Understanding the Contact Model

File:

src/models/Contact.ts

Code:

export interface Contact {
id: number;
name: string;
email: string;
company: string;
phone: string;
}

This is a TypeScript interface.

It defines the exact shape of a contact object.

This means every contact must contain:

id
name
email
company
phone

TypeScript is extremely important in enterprise React applications because it provides:

  • type safety
  • autocomplete
  • safer refactoring
  • fewer runtime bugs
  • clearer architecture

Without TypeScript, mistakes such as this would be easier:

name: 123

TypeScript immediately detects this problem.


Understanding Initial Data

File:

src/data/initialContacts.ts

This file stores the initial contact list.

export const initialContacts: Contact[] = [...]

The important concept here is:

The UI is generated from data.

React applications are usually data-driven.

Instead of manually creating HTML cards one by one, React transforms data into UI components.


Understanding App.tsx

The most important file is:

src/App.tsx

This component orchestrates the entire application.


The First Important useState

const [contacts, setContacts] =
useState<Contact[]>(initialContacts);

This is one of the most important lines in the entire app.

It introduces React state.


Understanding useState

useState gives memory to a component.

Without state, React components are static.

State allows components to:

  • remember values
  • react to user interaction
  • trigger re-rendering

The syntax:

const [contacts, setContacts]

creates two things:

VariablePurpose
contactscurrent state value
setContactsfunction to update state

This is fundamental React syntax.


Why React Re-Renders

When this happens:

setContacts(...)

React:

  1. updates the state
  2. re-runs the component function
  3. generates new JSX
  4. updates the browser DOM

This is why the UI automatically changes.


The Search State

const [search, setSearch] =
useState("");

This state stores the current search text.

Again:

  • search = current value
  • setSearch = state updater

Controlled Inputs Explained

Inside SearchBar.tsx:

<Input
value={search}
onChange={(_, data) =>
onSearchChange(data.value)
}
/>

This is called a:

Controlled Input

The input value is fully controlled by React state.

The flow becomes:

User types
onChange fires
setSearch updates state
React re-renders
Input displays new value

This is one of the most important React patterns.


Why Controlled Inputs Matter

Controlled inputs provide:

  • synchronization
  • validation
  • predictability
  • centralized state management

Enterprise applications almost always use controlled forms.


Understanding the Contact Form

File:

src/components/ContactForm.tsx

The form contains multiple state variables:

const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [company, setCompany] = useState("");
const [phone, setPhone] = useState("");

Each input field has its own controlled state.


Understanding the Submit Flow

Inside:

function handleSubmit()

we create a new contact object:

const newContact: Contact = {
id: Date.now(),
name,
email,
company,
phone,
};

This object is then sent upward:

onAddContact(newContact);

This is extremely important.


Parent → Child vs Child → Parent Communication

React props normally flow:

Parent → Child

But forms often need to send data back upward.

That happens through callback props.

Flow:

App.tsx
passes onAddContact
ContactForm.tsx
calls onAddContact(newContact)
App.tsx
receives the new contact

This is standard React architecture.


Understanding Array Updates

Inside App.tsx:

setContacts((previousContacts) => [
contact,
...previousContacts,
]);

This introduces immutable array updates.

React state should NOT be mutated directly.

Wrong:

contacts.push(contact)

Correct:

[contact, ...previousContacts]

This creates a NEW array.

React relies heavily on immutability.


Why Immutability Matters

Immutability helps React:

  • detect changes
  • optimize rendering
  • avoid bugs
  • keep updates predictable

This is one of the most important React concepts.


Understanding Derived State

This part is extremely important:

const filteredContacts =
contacts.filter((contact) =>
contact.name
.toLowerCase()
.includes(search.toLowerCase())
);

Notice:

filteredContacts
is NOT state

This is called:

Derived State

The filtered list is calculated from:

  • contacts
  • search

This follows React Learn best practices:

Avoid duplicating state unnecessarily.

Why Derived State Is Better

Wrong approach:

contacts state
filteredContacts state

This creates synchronization problems.

Correct approach:

contacts state
+
search state
derived filtered list

This is cleaner and more predictable.


Understanding List Rendering

Inside ContactList.tsx:

contacts.map((contact) => (
<ContactCard
key={contact.id}
contact={contact}
/>
))

This transforms:

  • data
    into
  • UI

This is declarative rendering.


Understanding React Keys

key={contact.id}

Keys help React identify list items.

They are critical for:

  • performance
  • stable rendering
  • proper DOM reconciliation

Without stable keys, React may render inefficiently.


Understanding ContactCard

File:

src/components/ContactCard.tsx

This component is responsible for displaying one contact.

This is component responsibility separation.


Why Componentization Matters

Instead of creating everything inside App.tsx:

App
→ ContactList
→ ContactCard

This creates:

  • reusable architecture
  • easier maintenance
  • cleaner code
  • scalability

This is the heart of React architecture.


Understanding Fluent UI Components

The app uses:

  • Card
  • CardHeader
  • Avatar
  • Input
  • Button
  • Text
  • Title1

These components already provide:

  • accessibility
  • spacing
  • keyboard support
  • typography
  • Microsoft design language

This is why Fluent UI is important in enterprise React development.


Understanding the Avatar Component

<Avatar
name={contact.name}
color="colorful"
/>

The Avatar automatically generates initials from the contact name.

This is extremely common in Microsoft-style applications.


Understanding the Layout

The root layout uses:

minHeight: "100vh"

This ensures full screen height.

The app also uses:

maxWidth: "1200px",
margin: "0 auto"

This centers the content.


Understanding the Responsive Grid

Inside ContactList:

gridTemplateColumns:
"repeat(auto-fit, minmax(300px, 1fr))"

This creates a responsive layout.

Meaning:

Create as many columns as fit.
Each column must be at least 300px wide.

This automatically adapts to screen size.


Why No useEffect Yet?

One of the best design decisions in this app is:

No useEffect

Why?

Because there are no external systems yet.

React Learn strongly emphasizes:

Effects should synchronize with external systems.

This app only manages internal UI state.

Therefore:

  • useState is enough
  • useEffect would be unnecessary

This is excellent React architecture.


Understanding the Complete React Flow

The complete application flow is:

User types into form
onChange triggers
setState updates
React re-renders
Contact list updates

This is modern React.


Production Validation

Run:

npm run build

This validates:

  • TypeScript
  • imports
  • JSX compilation
  • production build

Always validate production builds.


Technical Summary

ConceptPurpose
useStateComponent memory
Controlled InputsSynchronize UI and state
Derived StateCalculate filtered data
Immutable UpdatesSafe React state updates
map()Render arrays
filter()Dynamic filtering
Fluent UIEnterprise UI components
TypeScript InterfacesStrong typing
Callback PropsChild → parent communication
Component CompositionScalable architecture

Official Documentation

React

Fluent UI

Vite

TypeScript


Current Project Progress

BlockAppNameStatus
Block 101Hello React FluentCompleted
Block 102Profile CardCompleted
Block 103Product ListCompleted
Block 104Microsoft Style User CardCompleted
Block 105Static DashboardCompleted
Block 106Corporate Sidebar MenuCompleted
Block 107Visual Task ListCompleted
Block 221Modern CounterCompleted
Block 222Toggle ThemeCompleted
Block 223React CalculatorCompleted
Block 224Login FormCompleted
Block 225User RegistrationCompleted
Block 226ToDo ListCompleted
Block 227Shopping ListCompleted
Block 228Product FilterCompleted
Block 229Employee SearchCompleted
Block 230Shopping CartCompleted
Block 231Grade SimulatorCompleted
Block 232Inventory ControlCompleted
Block 233Contact AgendaCurrent

Edvaldo Guimrães Filho Avatar

Published by