React - The Industry Standard That Pays the Bills

React - The Industry Standard That Pays the Bills

Here’s the thing about React - it’s not necessarily better than Vue, but it’s everywhere. Learning React isn’t just about the technology; it’s about career opportunities.

I’ve been in hiring meetings where React experience was the deciding factor. Not because React is objectively better, but because team velocity matters when you’re shipping features every sprint. When your team already knows React, bringing in someone who can hit the ground running is invaluable.

Why React Won (And What That Means for Your Career)

React didn’t win because it was perfect - it won because Facebook had the resources to build an entire ecosystem around it. When you learn React, you’re not just learning a library; you’re learning an approach that’s used by Netflix, Airbnb, Instagram, WhatsApp, and thousands of companies worldwide.

Facebook’s Influence and Ecosystem

React benefits from having a massive company behind it:

  • Continuous development with full-time engineers
  • Extensive tooling like Create React App, React DevTools
  • Large community producing tutorials, packages, and solutions
  • Enterprise adoption driven by Facebook’s credibility

Job Market Reality

Let’s be honest about the numbers. According to Stack Overflow’s developer survey, React consistently ranks as one of the most wanted and loved frameworks. More importantly for your career:

  • React jobs often pay 15-20% more than similar positions
  • There are significantly more React positions available
  • Remote work opportunities are more common with React experience

The Component-First Philosophy

React popularized thinking about UIs as components - self-contained pieces that manage their own state and rendering. This wasn’t revolutionary (other frameworks did it too), but React made it mainstream and shaped how we build modern web applications.

React Fundamentals Through Familiar Territory

You’ve built this task manager with vanilla JavaScript and Vue. Now let’s see how React handles the same problems, and why its approach became the industry standard.

Setting Up React

We’ll use Create React App to get started quickly:

1npx create-react-app task-manager
2cd task-manager
3npm start

This gives you a complete development environment with hot reloading, testing, and build optimization - no configuration required.

JSX - HTML in JavaScript That Makes Sense

JSX looks weird at first, but it’s just JavaScript that lets you write HTML-like syntax:

 1function TaskItem({ task, onToggle, onDelete }) {
 2  return (
 3    <li className={`task-item ${task.completed ? 'completed' : ''}`}>
 4      <input 
 5        type="checkbox" 
 6        checked={task.completed}
 7        onChange={(e) => onToggle(task.id, e.target.checked)}
 8        className="task-checkbox"
 9      />
10      <div className="task-content">
11        <div className="task-title">{task.title}</div>
12        {task.description && (
13          <div className="task-description">{task.description}</div>
14        )}
15        <div className="task-meta">
16          <span className={`priority-badge priority-${task.priority}`}>
17            {task.priority}
18          </span>
19          <span>Created {formatDate(task.created_at)}</span>
20        </div>
21      </div>
22      <div className="task-actions">
23        <button onClick={() => onDelete(task.id)} className="delete-btn">
24          Delete
25        </button>
26      </div>
27    </li>
28  );
29}

Notice how similar this looks to our Vue template, but it’s actually JavaScript. You can use JavaScript expressions anywhere inside {}.

Components as Functions vs Classes

Modern React uses functional components with hooks. This is cleaner and more intuitive than the old class-based approach:

 1// Modern functional component (use this)
 2function TaskManager() {
 3  const [tasks, setTasks] = useState([]);
 4  const [loading, setLoading] = useState(false);
 5
 6  return (
 7    <div className="task-manager">
 8      {/* Component JSX */}
 9    </div>
10  );
11}
12
13// Old class component (don't use this anymore)
14class TaskManager extends React.Component {
15  constructor(props) {
16    super(props);
17    this.state = { tasks: [], loading: false };
18  }
19
20  render() {
21    return <div className="task-manager">{/* ... */}</div>;
22  }
23}

Our Task Manager, React Edition

Let’s build the complete task manager step by step. We’ll organize it into components and use modern React patterns:

API Service (Same as Before)

 1// src/services/api.js
 2const API_BASE_URL = 'https://api.taskmanager.dev/v1';
 3
 4class ApiError extends Error {
 5  constructor(message, status) {
 6    super(message);
 7    this.status = status;
 8  }
 9}
10
11async function apiRequest(endpoint, options = {}) {
12  try {
13    const response = await fetch(`${API_BASE_URL}${endpoint}`, {
14      headers: {
15        'Content-Type': 'application/json',
16        ...options.headers
17      },
18      ...options
19    });
20
21    if (!response.ok) {
22      let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
23      
24      try {
25        const errorData = await response.json();
26        errorMessage = errorData.message || errorMessage;
27      } catch {
28        // Use the default message if response isn't JSON
29      }
30      
31      throw new ApiError(errorMessage, response.status);
32    }
33
34    return await response.json();
35  } catch (error) {
36    if (error instanceof ApiError) {
37      throw error;
38    }
39    
40    // Network or other errors
41    throw new ApiError('Network error - check your connection', 0);
42  }
43}
44
45export const api = {
46  getTasks: (filters = {}) => {
47    const params = new URLSearchParams();
48    Object.entries(filters).forEach(([key, value]) => {
49      if (value != null) params.append(key, value);
50    });
51    
52    const query = params.toString() ? `?${params}` : '';
53    return apiRequest(`/tasks${query}`);
54  },
55
56  createTask: (taskData) => 
57    apiRequest('/tasks', {
58      method: 'POST',
59      body: JSON.stringify(taskData)
60    }),
61
62  updateTask: (id, updates) => 
63    apiRequest(`/tasks/${id}`, {
64      method: 'PUT',
65      body: JSON.stringify(updates)
66    }),
67
68  deleteTask: (id) => 
69    apiRequest(`/tasks/${id}`, {
70      method: 'DELETE'
71    })
72};

Task Item Component

 1// src/components/TaskItem.jsx
 2import React from 'react';
 3
 4function formatDate(dateString) {
 5  return new Date(dateString).toLocaleDateString('en-US', {
 6    month: 'short',
 7    day: 'numeric',
 8    hour: '2-digit',
 9    minute: '2-digit'
10  });
11}
12
13function TaskItem({ task, onToggle, onDelete }) {
14  return (
15    <li className={`task-item ${task.completed ? 'completed' : ''}`}>
16      <input 
17        type="checkbox" 
18        checked={task.completed}
19        onChange={(e) => onToggle(task.id, e.target.checked)}
20        className="task-checkbox"
21      />
22      <div className="task-content">
23        <div className="task-title">{task.title}</div>
24        {task.description && (
25          <div className="task-description">{task.description}</div>
26        )}
27        <div className="task-meta">
28          <span className={`priority-badge priority-${task.priority}`}>
29            {task.priority}
30          </span>
31          <span>Created {formatDate(task.created_at)}</span>
32        </div>
33      </div>
34      <div className="task-actions">
35        <button 
36          onClick={() => onDelete(task.id)} 
37          className="delete-btn"
38        >
39          Delete
40        </button>
41      </div>
42    </li>
43  );
44}
45
46export default TaskItem;

Add Task Form Component

 1// src/components/AddTaskForm.jsx
 2import React, { useState } from 'react';
 3
 4function AddTaskForm({ onAddTask, isLoading }) {
 5  const [formData, setFormData] = useState({
 6    title: '',
 7    description: '',
 8    priority: 'medium'
 9  });
10
11  const handleSubmit = async (e) => {
12    e.preventDefault();
13    
14    if (!formData.title.trim()) {
15      return;
16    }
17
18    try {
19      await onAddTask({
20        title: formData.title.trim(),
21        description: formData.description.trim() || null,
22        priority: formData.priority,
23        completed: false
24      });
25      
26      // Clear form on success
27      setFormData({
28        title: '',
29        description: '',
30        priority: 'medium'
31      });
32    } catch (error) {
33      // Error handling is done by parent component
34    }
35  };
36
37  const handleInputChange = (e) => {
38    const { name, value } = e.target;
39    setFormData(prev => ({
40      ...prev,
41      [name]: value
42    }));
43  };
44
45  return (
46    <form onSubmit={handleSubmit} className="add-task-form">
47      <div className="form-group">
48        <input 
49          type="text"
50          name="title"
51          value={formData.title}
52          onChange={handleInputChange}
53          placeholder="What needs to be done?" 
54          required
55        />
56        <select 
57          name="priority"
58          value={formData.priority}
59          onChange={handleInputChange}
60        >
61          <option value="low">Low Priority</option>
62          <option value="medium">Medium Priority</option>
63          <option value="high">High Priority</option>
64        </select>
65      </div>
66      <div className="form-group">
67        <textarea 
68          name="description"
69          value={formData.description}
70          onChange={handleInputChange}
71          placeholder="Add a description (optional)"
72          rows="2"
73        />
74      </div>
75      <button type="submit" disabled={isLoading}>
76        {isLoading ? 'Adding...' : 'Add Task'}
77      </button>
78    </form>
79  );
80}
81
82export default AddTaskForm;

Filter Buttons Component

 1// src/components/FilterButtons.jsx
 2import React from 'react';
 3
 4const FILTERS = [
 5  { key: 'all', label: 'All' },
 6  { key: 'pending', label: 'Pending' },
 7  { key: 'completed', label: 'Completed' }
 8];
 9
10function FilterButtons({ currentFilter, onFilterChange }) {
11  return (
12    <div className="filters">
13      {FILTERS.map(filter => (
14        <button 
15          key={filter.key}
16          className={`filter-btn ${currentFilter === filter.key ? 'active' : ''}`}
17          onClick={() => onFilterChange(filter.key)}
18        >
19          {filter.label}
20        </button>
21      ))}
22    </div>
23  );
24}
25
26export default FilterButtons;

Task List Component

 1// src/components/TaskList.jsx
 2import React from 'react';
 3import TaskItem from './TaskItem';
 4
 5function TaskList({ tasks, loading, filter, onToggleTask, onDeleteTask }) {
 6  if (loading) {
 7    return (
 8      <div className="loading">
 9        <div className="spinner"></div>
10        <p>Loading tasks...</p>
11      </div>
12    );
13  }
14
15  if (tasks.length === 0) {
16    const message = filter === 'all' 
17      ? 'Add your first task above to get started!'
18      : `No ${filter} tasks at the moment.`;
19      
20    return (
21      <div className="empty-state">
22        <h3>No tasks found</h3>
23        <p>{message}</p>
24      </div>
25    );
26  }
27
28  return (
29    <ul className="task-list">
30      {tasks.map(task => (
31        <TaskItem 
32          key={task.id}
33          task={task}
34          onToggle={onToggleTask}
35          onDelete={onDeleteTask}
36        />
37      ))}
38    </ul>
39  );
40}
41
42export default TaskList;

Error Message Component

 1// src/components/ErrorMessage.jsx
 2import React, { useEffect } from 'react';
 3
 4function ErrorMessage({ error, onClear }) {
 5  useEffect(() => {
 6    if (error) {
 7      const timer = setTimeout(() => {
 8        onClear();
 9      }, 5000);
10      
11      return () => clearTimeout(timer);
12    }
13  }, [error, onClear]);
14
15  if (!error) return null;
16
17  return (
18    <div className="error-message">
19      {error}
20      <button 
21        onClick={onClear}
22        style={{ 
23          marginLeft: '10px', 
24          background: 'rgba(255,255,255,0.2)',
25          border: 'none',
26          color: 'white',
27          padding: '2px 8px',
28          borderRadius: '3px',
29          cursor: 'pointer'
30        }}
31      >
32        ×
33      </button>
34    </div>
35  );
36}
37
38export default ErrorMessage;

Main App Component with Hooks

This is where React’s modern approach really shines. We use hooks to manage state and side effects:

  1// src/App.jsx
  2import React, { useState, useEffect, useMemo } from 'react';
  3import { api } from './services/api';
  4import AddTaskForm from './components/AddTaskForm';
  5import FilterButtons from './components/FilterButtons';
  6import TaskList from './components/TaskList';
  7import ErrorMessage from './components/ErrorMessage';
  8import './App.css';
  9
 10function App() {
 11  // State management
 12  const [tasks, setTasks] = useState([]);
 13  const [currentFilter, setCurrentFilter] = useState('all');
 14  const [loading, setLoading] = useState(false);
 15  const [error, setError] = useState('');
 16
 17  // Computed values with useMemo
 18  const filteredTasks = useMemo(() => {
 19    switch (currentFilter) {
 20      case 'completed':
 21        return tasks.filter(task => task.completed);
 22      case 'pending':
 23        return tasks.filter(task => !task.completed);
 24      default:
 25        return tasks;
 26    }
 27  }, [tasks, currentFilter]);
 28
 29  const taskStats = useMemo(() => {
 30    const total = tasks.length;
 31    const completed = tasks.filter(t => t.completed).length;
 32    return { total, completed };
 33  }, [tasks]);
 34
 35  // Load tasks on component mount
 36  useEffect(() => {
 37    loadTasks();
 38  }, []);
 39
 40  // API functions
 41  const loadTasks = async () => {
 42    try {
 43      setLoading(true);
 44      setError('');
 45      const response = await api.getTasks();
 46      setTasks(response.tasks || response);
 47    } catch (err) {
 48      setError('Failed to load tasks. Please try again.');
 49    } finally {
 50      setLoading(false);
 51    }
 52  };
 53
 54  const handleAddTask = async (taskData) => {
 55    try {
 56      setLoading(true);
 57      setError('');
 58      const newTask = await api.createTask(taskData);
 59      setTasks(prev => [newTask, ...prev]);
 60    } catch (err) {
 61      setError('Failed to create task. Please try again.');
 62      throw err; // Re-throw so form knows it failed
 63    } finally {
 64      setLoading(false);
 65    }
 66  };
 67
 68  const handleToggleTask = async (taskId, completed) => {
 69    const task = tasks.find(t => t.id === taskId);
 70    if (!task) return;
 71
 72    // Optimistic update
 73    const originalCompleted = task.completed;
 74    setTasks(prev => 
 75      prev.map(t => 
 76        t.id === taskId ? { ...t, completed } : t
 77      )
 78    );
 79
 80    try {
 81      await api.updateTask(taskId, { ...task, completed });
 82    } catch (err) {
 83      // Revert on failure
 84      setTasks(prev => 
 85        prev.map(t => 
 86          t.id === taskId ? { ...t, completed: originalCompleted } : t
 87        )
 88      );
 89      setError('Failed to update task. Please try again.');
 90    }
 91  };
 92
 93  const handleDeleteTask = async (taskId) => {
 94    if (!window.confirm('Are you sure you want to delete this task?')) {
 95      return;
 96    }
 97
 98    // Optimistic update
 99    const originalTasks = tasks;
100    setTasks(prev => prev.filter(t => t.id !== taskId));
101
102    try {
103      await api.deleteTask(taskId);
104    } catch (err) {
105      // Revert on failure
106      setTasks(originalTasks);
107      setError('Failed to delete task. Please try again.');
108    }
109  };
110
111  return (
112    <div className="container">
113      <header>
114        <h1>React Task Manager</h1>
115        <div className="stats">
116          <span>{taskStats.total} tasks</span>
117          <span>{taskStats.completed} completed</span>
118        </div>
119      </header>
120
121      <AddTaskForm 
122        onAddTask={handleAddTask}
123        isLoading={loading}
124      />
125
126      <FilterButtons 
127        currentFilter={currentFilter}
128        onFilterChange={setCurrentFilter}
129      />
130
131      <ErrorMessage 
132        error={error}
133        onClear={() => setError('')}
134      />
135
136      <TaskList 
137        tasks={filteredTasks}
138        loading={loading}
139        filter={currentFilter}
140        onToggleTask={handleToggleTask}
141        onDeleteTask={handleDeleteTask}
142      />
143    </div>
144  );
145}
146
147export default App;

React Hooks - The Modern Way

React hooks changed everything about how we write React components. Let’s break down the key hooks we’re using:

useState - Managing Component State

1const [tasks, setTasks] = useState([]);
2
3// Functional update pattern
4setTasks(prevTasks => [...prevTasks, newTask]);
5
6// Direct update
7setTasks(newTaskArray);

useEffect - Side Effects and Lifecycle

 1// Run once on mount (like componentDidMount)
 2useEffect(() => {
 3  loadTasks();
 4}, []);
 5
 6// Run when dependencies change
 7useEffect(() => {
 8  console.log('Tasks changed:', tasks);
 9}, [tasks]);
10
11// Cleanup (like componentWillUnmount)
12useEffect(() => {
13  const timer = setTimeout(() => setError(''), 5000);
14  return () => clearTimeout(timer);
15}, [error]);

useMemo - Performance Optimization

 1// Only recalculate when tasks or currentFilter changes
 2const filteredTasks = useMemo(() => {
 3  switch (currentFilter) {
 4    case 'completed':
 5      return tasks.filter(task => task.completed);
 6    case 'pending':
 7      return tasks.filter(task => !task.completed);
 8    default:
 9      return tasks;
10  }
11}, [tasks, currentFilter]);

Custom Hooks (Advanced Pattern)

You could extract the task management logic into a custom hook:

 1// src/hooks/useTasks.js
 2import { useState, useEffect, useMemo } from 'react';
 3import { api } from '../services/api';
 4
 5export function useTasks() {
 6  const [tasks, setTasks] = useState([]);
 7  const [loading, setLoading] = useState(false);
 8  const [error, setError] = useState('');
 9
10  const loadTasks = async () => {
11    try {
12      setLoading(true);
13      setError('');
14      const response = await api.getTasks();
15      setTasks(response.tasks || response);
16    } catch (err) {
17      setError('Failed to load tasks. Please try again.');
18    } finally {
19      setLoading(false);
20    }
21  };
22
23  useEffect(() => {
24    loadTasks();
25  }, []);
26
27  const addTask = async (taskData) => {
28    try {
29      setLoading(true);
30      setError('');
31      const newTask = await api.createTask(taskData);
32      setTasks(prev => [newTask, ...prev]);
33    } catch (err) {
34      setError('Failed to create task.');
35      throw err;
36    } finally {
37      setLoading(false);
38    }
39  };
40
41  return {
42    tasks,
43    loading,
44    error,
45    setError,
46    addTask,
47    updateTask: (id, updates) => { /* implementation */ },
48    deleteTask: (id) => { /* implementation */ }
49  };
50}
51
52// Use in component
53function App() {
54  const { tasks, loading, error, addTask } = useTasks();
55  // ... rest of component
56}

Props and State - React’s Data Flow

React enforces unidirectional data flow. Data flows down through props, and changes flow up through callback functions:

 1// Parent passes data down and callbacks up
 2<TaskList 
 3  tasks={filteredTasks}        // Data flows down
 4  onToggleTask={handleToggle}  // Events flow up
 5  onDeleteTask={handleDelete}  // Events flow up
 6/>
 7
 8// Child receives props and calls callbacks
 9function TaskList({ tasks, onToggleTask, onDeleteTask }) {
10  return (
11    <ul>
12      {tasks.map(task => (
13        <TaskItem 
14          key={task.id}
15          task={task}                    // Data flows down
16          onToggle={onToggleTask}        // Callbacks flow down
17          onDelete={onDeleteTask}        // Callbacks flow down
18        />
19      ))}
20    </ul>
21  );
22}

Event Handling and Form Patterns

React has specific patterns for handling events and forms:

 1// Event handlers receive synthetic events
 2const handleInputChange = (e) => {
 3  const { name, value } = e.target;
 4  setFormData(prev => ({
 5    ...prev,
 6    [name]: value
 7  }));
 8};
 9
10// Controlled components (recommended)
11<input 
12  value={formData.title}
13  onChange={handleInputChange}
14  name="title"
15/>
16
17// Uncontrolled components (use sparingly)
18const inputRef = useRef();
19<input ref={inputRef} defaultValue="initial" />

Performance Considerations

React is fast, but you can make it faster:

Key Props for List Rendering

1// Good - stable, unique keys
2{tasks.map(task => (
3  <TaskItem key={task.id} task={task} />
4))}
5
6// Bad - array index as key (can cause bugs)
7{tasks.map((task, index) => (
8  <TaskItem key={index} task={task} />
9))}

Avoiding Unnecessary Re-renders

 1// This creates a new function on every render (bad)
 2<button onClick={() => handleDelete(task.id)}>Delete</button>
 3
 4// Better - use useCallback for expensive operations
 5const handleDelete = useCallback((id) => {
 6  return () => {
 7    if (confirm('Delete task?')) {
 8      deleteTask(id);
 9    }
10  };
11}, [deleteTask]);
12
13<button onClick={handleDelete(task.id)}>Delete</button>

React.memo for Component Optimization

1// Only re-render if props actually changed
2const TaskItem = React.memo(function TaskItem({ task, onToggle, onDelete }) {
3  return (
4    // component JSX
5  );
6});

React Developer Tools - Your Debug Companion

Install the React Developer Tools browser extension. It shows you:

  • Component hierarchy and how props flow
  • State and hooks for each component in real-time
  • Performance profiling to identify slow renders
  • Time-travel debugging with component state history

React vs Vue vs Vanilla - The Final Comparison

Now that you’ve built the same app three different ways, here’s the honest comparison:

Development Speed

  • Vanilla: Slowest initial development, but you control everything
  • Vue: Fastest to learn and be productive
  • React: Steeper learning curve, but huge ecosystem support

Performance

  • Vanilla: Fastest possible performance (if you optimize correctly)
  • Vue: Excellent performance with minimal configuration
  • React: Good performance, but requires more optimization awareness

Job Market

  • Vanilla: Essential foundation, but limited job opportunities
  • Vue: Growing market, especially in smaller/medium companies
  • React: Largest job market and highest salaries

Learning Curve

  • Vanilla: Steep at first, but builds deep understanding
  • Vue: Gentle curve, most intuitive for beginners
  • React: Moderate curve, but concepts transfer to other frameworks

Bundle Size

  • Vanilla: Smallest possible bundle
  • Vue: Smaller bundle size (~34KB)
  • React: Larger bundle size (~42KB), but heavily optimized

When to Choose React

Choose React when:

  • Career opportunities are your priority
  • Team expertise - your team already knows React
  • Ecosystem needs - you need specific React libraries
  • Large-scale applications - React’s patterns scale well
  • Mobile development - React Native provides path to mobile

Industry Reality Check

Here’s what I’ve learned from hiring hundreds of developers:

React developers get hired faster because there are simply more React jobs. Companies choose React not because it’s perfect, but because:

  • It’s easy to find React developers
  • There’s extensive documentation and community support
  • The risk is lower when using a technology backed by Facebook
  • Most developers already have some React experience

But - companies are increasingly open to Vue and other frameworks. The key is understanding the fundamentals that transfer between all frameworks.

Beyond the Basics - Next Steps

Once you’re comfortable with React basics, explore:

Routing with React Router

 1import { BrowserRouter, Routes, Route } from 'react-router-dom';
 2
 3function App() {
 4  return (
 5    <BrowserRouter>
 6      <Routes>
 7        <Route path="/" element={<TaskList />} />
 8        <Route path="/completed" element={<CompletedTasks />} />
 9      </Routes>
10    </BrowserRouter>
11  );
12}

State Management with Redux or Zustand

1// Simple state management with Zustand
2import { create } from 'zustand';
3
4const useTaskStore = create((set) => ({
5  tasks: [],
6  addTask: (task) => set(state => ({ 
7    tasks: [...state.tasks, task] 
8  })),
9}));

Professional Development Setup

  • Next.js for full-stack React applications
  • TypeScript for better development experience
  • Testing Library for component testing
  • Storybook for component development

The Bottom Line

You’ve now built the same application four different ways. Each approach has its strengths:

  • REST APIs provide the foundation that supports any frontend
  • Vanilla JavaScript teaches you how everything actually works
  • Vue.js gives you productivity and a gentle learning curve
  • React provides career opportunities and industry standardization

The most important skill isn’t mastering any one framework - it’s understanding the patterns that work across all of them. HTTP requests, state management, component organization, error handling - these concepts transfer everywhere.

Your journey doesn’t end here. Pick the approach that matches your goals: Vue for rapid development and learning, React for career opportunities, or go deeper with vanilla JavaScript to truly understand the web platform.

But remember - underneath every framework is JavaScript doing exactly what you learned in the first article. Understanding these fundamentals makes you the kind of developer who can adapt to any team, any codebase, any challenge.

Welcome to the club. Now go build something amazing.