A Deep Dive into JavaScript, JSX, and TypeScript

Share on LinkedIn
Share on X
Share on Facebook
Share on WhatsApp
Share on Telegram
Share via Email
Copy Link

After spending countless hours reviewing code, mentoring teams, and building production applications, I've noticed a pattern: many developers aren't taking full advantage of modern web development tools. Let's change that! In this post, I'll show you exactly how JavaScript, JSX, and TypeScript work together to create robust, maintainable applications.

Remember when we used to write code like this?

Circa 2015
1var userList = document.getElementById('userList');
2var users = [];
3
4function addUser(name, email) {
5  users.push({ name: name, email: email });
6  renderUsers();
7}
8
9function renderUsers() {
10  userList.innerHTML = '';
11  for (var i = 0; i < users.length; i++) {
12    var li = document.createElement('li');
13    li.textContent = users[i].name + ' (' + users[i].email + ')';
14    userList.appendChild(li);
15  }
16}

Sure, it worked, but it was:

  • Hard to maintain
  • Prone to bugs
  • Difficult to scale

Let's see how we can transform this using our modern toolkit.

First, let's see how modern JavaScript features make our code cleaner and more reliable:

Modern JavaScript
1const UserManager = {
2  users: [],
3  
4  addUser({ name, email }) {
5    this.users = [...this.users, { name, email, id: crypto.randomUUID() }];
6    this.renderUsers();
7  },
8
9  removeUser(id) {
10    this.users = this.users.filter(user => user.id !== id);
11    this.renderUsers();
12  },
13
14  async fetchUsers() {
15    try {
16      const response = await fetch('/api/users');
17      this.users = await response.json();
18      this.renderUsers();
19    } catch (error) {
20      console.error('Failed to fetch users:', error);
21    }
22  },
23
24  renderUsers() {
25    const userList = document.getElementById('userList');
26    userList.innerHTML = this.users
27      .map(user => `<li>${user.name} (${user.email})</li>`)
28      .join('');
29  }
30};

Key improvements:

  • Object-oriented organization
  • Modern array methods
  • Destructuring
  • Async/await for API calls
  • Template literals for HTML

Now, let's transform this into a React component using JSX:

UserList.jsx
1import React, { useState, useEffect } from 'react';
2
3const UserCard = ({ user, onDelete }) => (
4  <div className="user-card">
5    <h3>{user.name}</h3>
6    <p>{user.email}</p>
7    <button 
8      onClick={() => onDelete(user.id)}
9      className="delete-btn"
10    >
11      Delete
12    </button>
13  </div>
14);
15
16const UserList = () => {
17  const [users, setUsers] = useState([]);
18  const [isLoading, setIsLoading] = useState(false);
19  const [error, setError] = useState(null);
20
21  useEffect(() => {
22    fetchUsers();
23  }, []);
24
25  const fetchUsers = async () => {
26    setIsLoading(true);
27    try {
28      const response = await fetch('/api/users');
29      const data = await response.json();
30      setUsers(data);
31    } catch (err) {
32      setError('Failed to fetch users');
33    } finally {
34      setIsLoading(false);
35    }
36  };
37
38  const deleteUser = async (id) => {
39    try {
40      await fetch(`/api/users/${id}`, { method: 'DELETE' });
41      setUsers(users.filter(user => user.id !== id));
42    } catch (err) {
43      setError('Failed to delete user');
44    }
45  };
46
47  if (isLoading) return <div>Loading...</div>;
48  if (error) return <div>Error: {error}</div>;
49
50  return (
51    <div className="user-list">
52      {users.map(user => (
53        <UserCard 
54          key={user.id}
55          user={user}
56          onDelete={deleteUser}
57        />
58      ))}
59    </div>
60  );
61};
62
63export default UserList;

Benefits of JSX:

  • Declarative UI components
  • Reusable components
  • Built-in state management
  • Clear data flow
  • Easy error handling

Finally, let's add TypeScript to make our code even more robust:

types.ts
1interface User {
2  id: string;
3  name: string;
4  email: string;
5  role?: 'admin' | 'user';
6}
7
8interface UserCardProps {
9  user: User;
10  onDelete: (id: string) => Promise<void>;
11}
12
13// UserList.tsx
14import React, { useState, useEffect } from 'react';
15import { User, UserCardProps } from './types';
16
17const UserCard: React.FC<UserCardProps> = ({ user, onDelete }) => (
18  <div className="user-card">
19    <h3>{user.name}</h3>
20    <p>{user.email}</p>
21    {user.role && <span className="role-badge">{user.role}</span>}
22    <button 
23      onClick={() => onDelete(user.id)}
24      className="delete-btn"
25    >
26      Delete
27    </button>
28  </div>
29);
30
31const UserList: React.FC = () => {
32  const [users, setUsers] = useState<User[]>([]);
33  const [isLoading, setIsLoading] = useState<boolean>(false);
34  const [error, setError] = useState<string | null>(null);
35
36  useEffect(() => {
37    fetchUsers();
38  }, []);
39
40  const fetchUsers = async (): Promise<void> => {
41    setIsLoading(true);
42    try {
43      const response = await fetch('/api/users');
44      const data: User[] = await response.json();
45      setUsers(data);
46    } catch (err) {
47      setError(err instanceof Error ? err.message : 'Failed to fetch users');
48    } finally {
49      setIsLoading(false);
50    }
51  };
52
53  const deleteUser = async (id: string): Promise<void> => {
54    try {
55      await fetch(`/api/users/${id}`, { method: 'DELETE' });
56      setUsers(users.filter(user => user.id !== id));
57    } catch (err) {
58      setError(err instanceof Error ? err.message : 'Failed to delete user');
59    }
60  };
61
62  if (isLoading) return <div>Loading...</div>;
63  if (error) return <div>Error: {error}</div>;
64
65  return (
66    <div className="user-list">
67      {users.map(user => (
68        <UserCard 
69          key={user.id}
70          user={user}
71          onDelete={deleteUser}
72        />
73      ))}
74    </div>
75  );
76};
77
78export default UserList;

TypeScript advantages:

  • Clear interface definitions
  • Compile-time error checking
  • Better IDE support
  • Self-documenting code
  • Easier refactoring
Article image

Let's create a complete feature using all three technologies. Here's a searchable, filterable user management system:

UserManagement.tsx
1import React, { useState, useEffect, useCallback } from 'react';
2import debounce from 'lodash/debounce';
3
4interface User {
5  id: string;
6  name: string;
7  email: string;
8  role: 'admin' | 'user';
9  lastActive: Date;
10}
11
12interface UserFilters {
13  role?: 'admin' | 'user';
14  searchTerm: string;
15  isActive: boolean;
16}
17
18const UserManagement: React.FC = () => {
19  const [users, setUsers] = useState<User[]>([]);
20  const [filters, setFilters] = useState<UserFilters>({
21    searchTerm: '',
22    isActive: true
23  });
24  const [isLoading, setIsLoading] = useState(false);
25
26  // Debounced search function
27  const debouncedSearch = useCallback(
28    debounce((term: string) => {
29      setFilters(prev => ({ ...prev, searchTerm: term }));
30    }, 300),
31    []
32  );
33
34  const fetchFilteredUsers = async () => {
35    setIsLoading(true);
36    try {
37      const queryParams = new URLSearchParams({
38        role: filters.role || '',
39        search: filters.searchTerm,
40        active: filters.isActive.toString()
41      });
42
43      const response = await fetch(`/api/users?${queryParams}`);
44      const data: User[] = await response.json();
45      setUsers(data);
46    } catch (error) {
47      console.error('Failed to fetch users:', error);
48    } finally {
49      setIsLoading(false);
50    }
51  };
52
53  useEffect(() => {
54    fetchFilteredUsers();
55  }, [filters]);
56
57  return (
58    <div className="user-management">
59      <div className="filters">
60        <input
61          type="text"
62          placeholder="Search users..."
63          onChange={e => debouncedSearch(e.target.value)}
64          className="search-input"
65        />
66        
67        <select
68          onChange={e => setFilters(prev => ({ 
69            ...prev, 
70            role: e.target.value as 'admin' | 'user' | undefined 
71          }))}
72        >
73          <option value="">All Roles</option>
74          <option value="admin">Admin</option>
75          <option value="user">User</option>
76        </select>
77
78        <label>
79          <input
80            type="checkbox"
81            checked={filters.isActive}
82            onChange={e => setFilters(prev => ({
83              ...prev,
84              isActive: e.target.checked
85            }))}
86          />
87          Active Users Only
88        </label>
89      </div>
90
91      {isLoading ? (
92        <div className="loading">Loading users...</div>
93      ) : (
94        <div className="user-grid">
95          {users.map(user => (
96            <UserCard
97              key={user.id}
98              user={user}
99              onUpdate={fetchFilteredUsers}
100            />
101          ))}
102        </div>
103      )}
104    </div>
105  );
106};
107
108export default UserManagement;

This example demonstrates:

  • Modern JavaScript features (async/await, destructuring)
  • JSX for component composition
  • TypeScript for type safety
  • Real-world patterns (debouncing, filtering)
  • Error handling
  • Loading states

1. State Management:

1// Bad
2const [userState, setUserState] = useState<any>({});
3
4// Good
5interface UserState {
6  data: User[];
7  isLoading: boolean;
8  error: Error | null;
9}
10
11const [userState, setUserState] = useState<UserState>({
12  data: [],
13  isLoading: false,
14  error: null
15});

2. Error Boundaries:

1class ErrorBoundary extends React.Component<
2  { children: React.ReactNode },
3  { hasError: boolean }
4> {
5  state = { hasError: false };
6
7  static getDerivedStateFromError() {
8    return { hasError: true };
9  }
10
11  render() {
12    if (this.state.hasError) {
13      return <h1>Something went wrong.</h1>;
14    }
15
16    return this.props.children;
17  }
18}

3. Custom Hooks:

1const useDebounce = <T>(value: T, delay: number): T => {
2  const [debouncedValue, setDebouncedValue] = useState<T>(value);
3
4  useEffect(() => {
5    const handler = setTimeout(() => {
6      setDebouncedValue(value);
7    }, delay);
8
9    return () => {
10      clearTimeout(handler);
11    };
12  }, [value, delay]);
13
14  return debouncedValue;
15};

The combination of JavaScript, JSX, and TypeScript gives us a powerful toolkit for building modern web applications. Each technology serves a specific purpose:

  • JavaScript provides the core functionality
  • JSX makes UI development intuitive
  • TypeScript adds safety and maintainability

Remember: Start small, add complexity as needed, and always consider the maintenance implications of your code choices.

Share on LinkedIn
Share on X
Share on Facebook
Share on WhatsApp
Share on Telegram
Share via Email
Copy Link

Ready to take your business to the next level? Let’s make it happen.