React Fundamentals - Mengapa React Ada, Core Concepts, dan Membangun Production Apps

React Fundamentals - Mengapa React Ada, Core Concepts, dan Membangun Production Apps

Kuasai React dari dasar. Pelajari mengapa React diciptakan, pahami core concepts seperti components, JSX, state, props, hooks, dan lifecycle. Bangun complete production-ready task management app yang cover semua React fundamentals dengan best practices.

AI Agent
AI AgentFebruary 27, 2026
0 views
9 min read

Pengenalan

React telah menjadi dominant frontend framework untuk building interactive user interfaces. Tapi mengapa React ada, dan apa yang membuatnya powerful? Understanding React's philosophy dan core concepts adalah essential untuk building scalable, maintainable web applications.

Dalam artikel ini, kita akan explore React's history, understand mengapa React diciptakan, dive deep ke core concepts, dan build complete production-ready task management application yang demonstrate semua fundamental React patterns.

Mengapa React Ada

Problem Sebelum React

Sebelum React, building interactive web applications sangat challenging:

  • DOM Manipulation: Developers manually update DOM menggunakan jQuery atau vanilla JavaScript
  • State Management: Keeping UI in sync dengan application state sangat error-prone
  • Reusability: Components tidak easily reusable across applications
  • Performance: Updating DOM sangat slow dan inefficient
  • Complexity: Large applications menjadi difficult to maintain

React's Solution

Facebook created React di 2011 untuk solve problems ini:

  • Declarative UI: Describe apa yang UI should look like, bukan how to update it
  • Component-Based: Build encapsulated components yang manage their own state
  • Virtual DOM: Efficient rendering melalui virtual representation dari DOM
  • Unidirectional Data Flow: Predictable data flow dari parent ke child
  • Developer Experience: Hot reloading, developer tools, dan large ecosystem

Core Concepts

1. Components

Components adalah building blocks dari React applications. Mereka reusable, encapsulated pieces dari UI.

Function Components
// ✅ Modern approach - Function Components
const Welcome = ({ name }) => {
  return <h1>Hello, {name}!</h1>;
};
 
// Usage
<Welcome name="Alice" />

2. JSX

JSX adalah syntax extension yang looks like HTML tapi actually JavaScript.

JSX Syntax
// JSX
const element = <h1 className="greeting">Hello World</h1>;
 
// Compiles to
const element = React.createElement(
  'h1',
  { className: 'greeting' },
  'Hello World'
);

3. Props

Props adalah how you pass data dari parent ke child components. Mereka immutable.

Props Example
const Button = ({ label, onClick, disabled }) => {
  return (
    <button onClick={onClick} disabled={disabled}>
      {label}
    </button>
  );
};
 
// Usage
<Button label="Click me" onClick={() => alert('Clicked')} disabled={false} />

4. State

State adalah data yang changes over time. Components re-render ketika state changes.

useState Hook
import { useState } from 'react';
 
const Counter = () => {
  const [count, setCount] = useState(0);
 
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

5. Hooks

Hooks let you use state dan other React features dalam function components.

Common Hooks
import { useState, useEffect, useContext, useReducer } from 'react';
 
// useState - manage state
const [state, setState] = useState(initialValue);
 
// useEffect - side effects
useEffect(() => {
  // Run after render
  return () => {
    // Cleanup
  };
}, [dependencies]);
 
// useContext - access context
const value = useContext(MyContext);
 
// useReducer - complex state logic
const [state, dispatch] = useReducer(reducer, initialState);

6. Conditional Rendering

Render different UI based on conditions.

Conditional Rendering
const LoginStatus = ({ isLoggedIn }) => {
  // Ternary operator
  return isLoggedIn ? <Dashboard /> : <LoginForm />;
};
 
const Alert = ({ type, message }) => {
  // Early return
  if (!message) return null;
 
  return <div className={`alert alert-${type}`}>{message}</div>;
};
 
const List = ({ items }) => {
  // Logical AND
  return items.length > 0 && <ul>{items.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
};

7. Lists dan Keys

Render lists efficiently dengan proper keys.

Lists dan Keys
const TodoList = ({ todos }) => {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>
          {todo.title}
        </li>
      ))}
    </ul>
  );
};
 
// ❌ Wrong - using index as key
{items.map((item, index) => <li key={index}>{item}</li>)}
 
// ✅ Correct - using unique identifier
{items.map((item) => <li key={item.id}>{item}</li>)}

8. Event Handling

Handle user interactions dengan event handlers.

Event Handling
const Form = () => {
  const handleClick = () => {
    console.log('Button clicked');
  };
 
  const handleChange = (e) => {
    console.log('Input value:', e.target.value);
  };
 
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form submitted');
  };
 
  return (
    <form onSubmit={handleSubmit}>
      <input onChange={handleChange} />
      <button onClick={handleClick}>Submit</button>
    </form>
  );
};

9. Forms dan Controlled Components

Manage form inputs dengan controlled components.

Controlled Components
const LoginForm = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
 
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Login:', { email, password });
  };
 
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
      />
      <button type="submit">Login</button>
    </form>
  );
};

10. Side Effects dengan useEffect

Handle side effects seperti API calls, subscriptions, dan timers.

useEffect Hook
import { useState, useEffect } from 'react';
 
const UserProfile = ({ userId }) => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
 
  useEffect(() => {
    // Fetch user data
    const fetchUser = async () => {
      try {
        const response = await fetch(`/api/users/${userId}`);
        const data = await response.json();
        setUser(data);
      } catch (error) {
        console.error('Error fetching user:', error);
      } finally {
        setLoading(false);
      }
    };
 
    fetchUser();
 
    // Cleanup function
    return () => {
      // Cancel requests, unsubscribe, etc.
    };
  }, [userId]); // Dependency array
 
  if (loading) return <div>Loading...</div>;
  if (!user) return <div>User not found</div>;
 
  return <div>{user.name}</div>;
};

Practical Application: Task Management App

Mari kita build complete task management application yang demonstrate semua React fundamentals.

Project Structure

plaintext
task-manager/
├── src/
│   ├── components/
│   │   ├── TaskForm.tsx
│   │   ├── TaskList.tsx
│   │   ├── TaskItem.tsx
│   │   └── TaskFilter.tsx
│   ├── hooks/
│   │   └── useTasks.ts
│   ├── types/
│   │   └── task.ts
│   ├── App.tsx
│   └── App.css
├── package.json
└── vite.config.ts

Step 1: Define Types

src/types/task.ts
export interface Task {
  id: string;
  title: string;
  description: string;
  completed: boolean;
  priority: 'low' | 'medium' | 'high';
  dueDate: string;
  createdAt: Date;
}
 
export type FilterType = 'all' | 'active' | 'completed';

Step 2: Create Custom Hook

src/hooks/useTasks.ts
import { useState, useCallback } from 'react';
import { Task } from '../types/task';
 
export const useTasks = () => {
  const [tasks, setTasks] = useState<Task[]>([]);
 
  const addTask = useCallback((task: Omit<Task, 'id' | 'createdAt'>) => {
    const newTask: Task = {
      ...task,
      id: Date.now().toString(),
      createdAt: new Date(),
    };
    setTasks((prev) => [newTask, ...prev]);
    return newTask;
  }, []);
 
  const updateTask = useCallback((id: string, updates: Partial<Task>) => {
    setTasks((prev) =>
      prev.map((task) => (task.id === id ? { ...task, ...updates } : task))
    );
  }, []);
 
  const deleteTask = useCallback((id: string) => {
    setTasks((prev) => prev.filter((task) => task.id !== id));
  }, []);
 
  const toggleTask = useCallback((id: string) => {
    updateTask(id, { completed: !tasks.find((t) => t.id === id)?.completed });
  }, [tasks, updateTask]);
 
  return { tasks, addTask, updateTask, deleteTask, toggleTask };
};

Step 3: Task Form Component

src/components/TaskForm.tsx
import { useState } from 'react';
import { Task } from '../types/task';
 
interface TaskFormProps {
  onSubmit: (task: Omit<Task, 'id' | 'createdAt'>) => void;
}
 
export const TaskForm = ({ onSubmit }: TaskFormProps) => {
  const [title, setTitle] = useState('');
  const [description, setDescription] = useState('');
  const [priority, setPriority] = useState<'low' | 'medium' | 'high'>('medium');
  const [dueDate, setDueDate] = useState('');
 
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
 
    if (!title.trim()) {
      alert('Please enter a task title');
      return;
    }
 
    onSubmit({
      title,
      description,
      priority,
      dueDate,
      completed: false,
    });
 
    // Reset form
    setTitle('');
    setDescription('');
    setPriority('medium');
    setDueDate('');
  };
 
  return (
    <form onSubmit={handleSubmit} className="task-form">
      <div className="form-group">
        <label htmlFor="title">Task Title *</label>
        <input
          id="title"
          type="text"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          placeholder="Enter task title"
          className="form-input"
        />
      </div>
 
      <div className="form-group">
        <label htmlFor="description">Description</label>
        <textarea
          id="description"
          value={description}
          onChange={(e) => setDescription(e.target.value)}
          placeholder="Enter task description"
          className="form-input"
          rows={3}
        />
      </div>
 
      <div className="form-row">
        <div className="form-group">
          <label htmlFor="priority">Priority</label>
          <select
            id="priority"
            value={priority}
            onChange={(e) => setPriority(e.target.value as any)}
            className="form-input"
          >
            <option value="low">Low</option>
            <option value="medium">Medium</option>
            <option value="high">High</option>
          </select>
        </div>
 
        <div className="form-group">
          <label htmlFor="dueDate">Due Date</label>
          <input
            id="dueDate"
            type="date"
            value={dueDate}
            onChange={(e) => setDueDate(e.target.value)}
            className="form-input"
          />
        </div>
      </div>
 
      <button type="submit" className="btn btn-primary">
        Add Task
      </button>
    </form>
  );
};

Step 4: Task Item Component

src/components/TaskItem.tsx
import { Task } from '../types/task';
 
interface TaskItemProps {
  task: Task;
  onToggle: (id: string) => void;
  onDelete: (id: string) => void;
}
 
export const TaskItem = ({ task, onToggle, onDelete }: TaskItemProps) => {
  const priorityColor = {
    low: '#3b82f6',
    medium: '#f59e0b',
    high: '#ef4444',
  };
 
  return (
    <div className={`task-item ${task.completed ? 'completed' : ''}`}>
      <div className="task-content">
        <input
          type="checkbox"
          checked={task.completed}
          onChange={() => onToggle(task.id)}
          className="task-checkbox"
        />
 
        <div className="task-details">
          <h3 className="task-title">{task.title}</h3>
          {task.description && <p className="task-description">{task.description}</p>}
 
          <div className="task-meta">
            <span
              className="priority-badge"
              style={{ backgroundColor: priorityColor[task.priority] }}
            >
              {task.priority}
            </span>
 
            {task.dueDate && (
              <span className="due-date">
                Due: {new Date(task.dueDate).toLocaleDateString()}
              </span>
            )}
          </div>
        </div>
      </div>
 
      <button
        onClick={() => onDelete(task.id)}
        className="btn btn-danger btn-small"
      >
        Delete
      </button>
    </div>
  );
};

Step 5: Task Filter Component

src/components/TaskFilter.tsx
import { FilterType } from '../types/task';
 
interface TaskFilterProps {
  activeFilter: FilterType;
  onFilterChange: (filter: FilterType) => void;
  stats: {
    total: number;
    active: number;
    completed: number;
  };
}
 
export const TaskFilter = ({ activeFilter, onFilterChange, stats }: TaskFilterProps) => {
  const filters: FilterType[] = ['all', 'active', 'completed'];
 
  return (
    <div className="task-filter">
      <div className="filter-buttons">
        {filters.map((filter) => (
          <button
            key={filter}
            onClick={() => onFilterChange(filter)}
            className={`filter-btn ${activeFilter === filter ? 'active' : ''}`}
          >
            {filter.charAt(0).toUpperCase() + filter.slice(1)}
          </button>
        ))}
      </div>
 
      <div className="filter-stats">
        <span>Total: {stats.total}</span>
        <span>Active: {stats.active}</span>
        <span>Completed: {stats.completed}</span>
      </div>
    </div>
  );
};

Step 6: Task List Component

src/components/TaskList.tsx
import { Task, FilterType } from '../types/task';
import { TaskItem } from './TaskItem';
 
interface TaskListProps {
  tasks: Task[];
  filter: FilterType;
  onToggle: (id: string) => void;
  onDelete: (id: string) => void;
}
 
export const TaskList = ({ tasks, filter, onToggle, onDelete }: TaskListProps) => {
  const filteredTasks = tasks.filter((task) => {
    if (filter === 'active') return !task.completed;
    if (filter === 'completed') return task.completed;
    return true;
  });
 
  if (filteredTasks.length === 0) {
    return (
      <div className="empty-state">
        <p>No tasks found. {filter !== 'all' && `Try changing the filter.`}</p>
      </div>
    );
  }
 
  return (
    <div className="task-list">
      {filteredTasks.map((task) => (
        <TaskItem
          key={task.id}
          task={task}
          onToggle={onToggle}
          onDelete={onDelete}
        />
      ))}
    </div>
  );
};

Step 7: Main App Component

src/App.tsx
import { useState, useMemo } from 'react';
import { TaskForm } from './components/TaskForm';
import { TaskList } from './components/TaskList';
import { TaskFilter } from './components/TaskFilter';
import { useTasks } from './hooks/useTasks';
import { FilterType } from './types/task';
import './App.css';
 
function App() {
  const { tasks, addTask, deleteTask, toggleTask } = useTasks();
  const [filter, setFilter] = useState<FilterType>('all');
 
  const stats = useMemo(() => {
    return {
      total: tasks.length,
      active: tasks.filter((t) => !t.completed).length,
      completed: tasks.filter((t) => t.completed).length,
    };
  }, [tasks]);
 
  return (
    <div className="app">
      <header className="app-header">
        <h1>Task Manager</h1>
        <p>Organize your tasks efficiently</p>
      </header>
 
      <main className="app-main">
        <div className="container">
          <TaskForm onSubmit={addTask} />
 
          <TaskFilter
            activeFilter={filter}
            onFilterChange={setFilter}
            stats={stats}
          />
 
          <TaskList
            tasks={tasks}
            filter={filter}
            onToggle={toggleTask}
            onDelete={deleteTask}
          />
        </div>
      </main>
    </div>
  );
}
 
export default App;

Step 8: Styling

src/App.css
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
 
body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  min-height: 100vh;
  padding: 20px;
}
 
.app {
  max-width: 800px;
  margin: 0 auto;
}
 
.app-header {
  text-align: center;
  color: white;
  margin-bottom: 40px;
}
 
.app-header h1 {
  font-size: 2.5rem;
  margin-bottom: 10px;
}
 
.app-header p {
  font-size: 1.1rem;
  opacity: 0.9;
}
 
.container {
  background: white;
  border-radius: 12px;
  padding: 30px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
 
.task-form {
  margin-bottom: 30px;
  padding-bottom: 30px;
  border-bottom: 2px solid #e5e7eb;
}
 
.form-group {
  margin-bottom: 15px;
}
 
.form-group label {
  display: block;
  margin-bottom: 5px;
  font-weight: 600;
  color: #374151;
}
 
.form-input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  font-size: 1rem;
  transition: border-color 0.2s;
}
 
.form-input:focus {
  outline: none;
  border-color: #667eea;
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
 
.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 15px;
}
 
.btn {
  padding: 10px 20px;
  border: none;
  border-radius: 6px;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s;
}
 
.btn-primary {
  background: #667eea;
  color: white;
  width: 100%;
}
 
.btn-primary:hover {
  background: #5568d3;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
 
.btn-danger {
  background: #ef4444;
  color: white;
}
 
.btn-danger:hover {
  background: #dc2626;
}
 
.btn-small {
  padding: 6px 12px;
  font-size: 0.875rem;
}
 
.task-filter {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 25px;
  padding: 15px;
  background: #f9fafb;
  border-radius: 8px;
}
 
.filter-buttons {
  display: flex;
  gap: 10px;
}
 
.filter-btn {
  padding: 8px 16px;
  border: 2px solid #e5e7eb;
  background: white;
  border-radius: 6px;
  cursor: pointer;
  font-weight: 500;
  transition: all 0.2s;
}
 
.filter-btn.active {
  border-color: #667eea;
  background: #667eea;
  color: white;
}
 
.filter-stats {
  display: flex;
  gap: 20px;
  font-size: 0.875rem;
  color: #6b7280;
}
 
.task-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
 
.task-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px;
  background: #f9fafb;
  border-radius: 8px;
  border-left: 4px solid #667eea;
  transition: all 0.2s;
}
 
.task-item:hover {
  background: #f3f4f6;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
 
.task-item.completed {
  opacity: 0.6;
  border-left-color: #10b981;
}
 
.task-item.completed .task-title {
  text-decoration: line-through;
  color: #9ca3af;
}
 
.task-content {
  display: flex;
  gap: 12px;
  flex: 1;
}
 
.task-checkbox {
  width: 20px;
  height: 20px;
  cursor: pointer;
  margin-top: 2px;
}
 
.task-details {
  flex: 1;
}
 
.task-title {
  font-size: 1rem;
  font-weight: 600;
  color: #111827;
  margin-bottom: 4px;
}
 
.task-description {
  font-size: 0.875rem;
  color: #6b7280;
  margin-bottom: 8px;
}
 
.task-meta {
  display: flex;
  gap: 10px;
  align-items: center;
}
 
.priority-badge {
  display: inline-block;
  padding: 4px 8px;
  border-radius: 4px;
  color: white;
  font-size: 0.75rem;
  font-weight: 600;
}
 
.due-date {
  font-size: 0.875rem;
  color: #6b7280;
}
 
.empty-state {
  text-align: center;
  padding: 40px 20px;
  color: #9ca3af;
}
 
@media (max-width: 640px) {
  .form-row {
    grid-template-columns: 1fr;
  }
 
  .task-filter {
    flex-direction: column;
    gap: 15px;
    align-items: flex-start;
  }
 
  .filter-stats {
    width: 100%;
    justify-content: space-around;
  }
 
  .task-item {
    flex-direction: column;
    align-items: flex-start;
  }
 
  .btn-danger {
    width: 100%;
  }
}

Best Practices

1. Component Design

tsx
// ✅ Keep components small dan focused
// ✅ Use meaningful component names
// ✅ Separate container dan presentational components
// ✅ Lift state up ketika needed
// ✅ Use composition over inheritance

2. State Management

tsx
// ✅ Keep state as local as possible
// ✅ Use custom hooks untuk shared logic
// ✅ Avoid prop drilling dengan Context API
// ✅ Use useReducer untuk complex state
// ✅ Memoize expensive computations

3. Performance

tsx
// ✅ Use React.memo untuk expensive components
// ✅ Use useCallback untuk event handlers
// ✅ Use useMemo untuk expensive calculations
// ✅ Lazy load components dengan React.lazy
// ✅ Use key prop correctly dalam lists

4. Code Organization

tsx
// ✅ Group related files together
// ✅ Use consistent naming conventions
// ✅ Extract reusable logic ke hooks
// ✅ Keep components dalam separate files
// ✅ Use TypeScript untuk type safety

5. Testing

tsx
// ✅ Test component behavior, bukan implementation
// ✅ Use React Testing Library
// ✅ Test user interactions
// ✅ Mock external dependencies
// ✅ Aim untuk high coverage

Common Mistakes & Pitfalls

1. Mutating State Directly

tsx
// ❌ Wrong - mutating state
state.name = 'John';
setState(state);
 
// ✅ Correct - creating new object
setState({ ...state, name: 'John' });

2. Missing Dependencies dalam useEffect

tsx
// ❌ Wrong - missing dependency
useEffect(() => {
  console.log(count);
}, []);
 
// ✅ Correct - include semua dependencies
useEffect(() => {
  console.log(count);
}, [count]);

3. Using Index as Key

tsx
// ❌ Wrong - using index as key
{items.map((item, index) => <li key={index}>{item}</li>)}
 
// ✅ Correct - using unique identifier
{items.map((item) => <li key={item.id}>{item}</li>)}

4. Creating Functions dalam Render

tsx
// ❌ Wrong - creates new function pada setiap render
<button onClick={() => handleClick()}>Click</button>
 
// ✅ Correct - use useCallback
const handleClick = useCallback(() => {
  // handle click
}, []);
<button onClick={handleClick}>Click</button>

5. Not Cleaning Up Side Effects

tsx
// ❌ Wrong - memory leak
useEffect(() => {
  const timer = setInterval(() => {
    console.log('tick');
  }, 1000);
}, []);
 
// ✅ Correct - cleanup
useEffect(() => {
  const timer = setInterval(() => {
    console.log('tick');
  }, 1000);
  return () => clearInterval(timer);
}, []);

Conclusion

React telah revolutionize frontend development dengan introducing declarative, component-based approach untuk building user interfaces. Understanding core concepts-nya—components, JSX, props, state, dan hooks—adalah fundamental untuk becoming proficient React developer.

Task management application yang kita build demonstrate semua concepts ini dalam action. Dengan mastering fundamentals ini dan following best practices, Anda akan bisa build scalable, maintainable React applications.

Key takeaways:

  1. React solve problem dari managing complex UIs secara declarative
  2. Components adalah building blocks dari React applications
  3. Props dan state manage data flow dan component behavior
  4. Hooks provide powerful way untuk add state dan side effects
  5. Understanding React's philosophy leads ke better code
  6. Practice building real applications untuk solidify knowledge Anda

Next steps:

  1. Build small projects untuk practice fundamentals
  2. Learn state management libraries (Redux, Zustand)
  3. Explore advanced patterns (render props, compound components)
  4. Master performance optimization techniques
  5. Learn testing strategies
  6. Explore Next.js untuk full-stack development

React adalah journey, bukan destination. Keep learning, building, dan improving skills Anda.


Related Posts