0%
Building Modern Applications with React

Building Modern Applications with React

A complete guide to building high-performance, maintainable web applications with React.

I

InSkillCoach

· min

Building Modern Applications with React

React is one of the most popular JavaScript libraries for building user interfaces. Developed and maintained by Facebook (Meta), it lets you build complex web applications with reusable components and efficient state management.

Why Choose React?

  • Declarative approach: You describe what your UI should look like, and React takes care of the updates
  • Component-based: Build encapsulated, reusable interface elements
  • Rich ecosystem: A wide range of complementary libraries and tools
  • Large community: Support, resources, and regular updates
  • Optimized performance: Virtual DOM and reconciliation for efficient updates
  • Adopted by major companies: Facebook, Instagram, Netflix, Airbnb, and more

Setting Up a React Project

Creating a Project with Create React App

Create React App is an official tool for quickly getting started with a React project:

# Installation
npx create-react-app mon-application

# Démarrage
cd mon-application
npm start

Using Vite (a Modern Alternative)

Vite offers faster build times and a better development experience:

# Installation
npm create vite@latest mon-application -- --template react

# Démarrage
cd mon-application
npm install
npm run dev

React Fundamentals

Components

Components are the basic building blocks of a React application:

// Function component
function Salutation(props) {
  return <h1>Bonjour, {props.nom} !</h1>;
}

// Usage
<Salutation nom="Alice" />

JSX

JSX is a syntax extension that lets you write HTML inside JavaScript:

const element = <h1>Bonjour, monde !</h1>;

// With JavaScript expressions
const nom = "Alice";
const element = <h1>Bonjour, {nom} !</h1>;

// With attributes
const element = <img src={user.avatarUrl} alt="Avatar" />;

// Nested elements
const element = (
  <div>
    <h1>Titre</h1>
    <p>Paragraphe</p>
  </div>
);

Props

Props let you pass data from a parent component to a child component:

// Component definition
function Carte(props) {
  return (
    <div className="carte">
      <h2>{props.titre}</h2>
      <p>{props.description}</p>
      {props.children}
    </div>
  );
}

// Usage
<Carte 
  titre="Mon titre" 
  description="Ma description"
>
  <button>En savoir plus</button>
</Carte>

State

State lets you store data that can change over time:

import { useState } from 'react';

function Compteur() {
  // State declaration
  const [compteur, setCompteur] = useState(0);
  
  return (
    <div>
      <p>Vous avez cliqué {compteur} fois</p>
      <button onClick={() => setCompteur(compteur + 1)}>
        Cliquez ici
      </button>
    </div>
  );
}

Lifecycle with Hooks

Hooks let you use state and other React features in function components:

import { useState, useEffect } from 'react';

function ExempleEffet() {
  const [compteur, setCompteur] = useState(0);
  
  // Similar to componentDidMount and componentDidUpdate
  useEffect(() => {
    // Update the document title
    document.title = `Vous avez cliqué ${compteur} fois`;
    
    // Cleanup function (similar to componentWillUnmount)
    return () => {
      document.title = 'React App';
    };
  }, [compteur]); // Runs only if compteur changes
  
  return (
    <div>
      <p>Vous avez cliqué {compteur} fois</p>
      <button onClick={() => setCompteur(compteur + 1)}>
        Cliquez ici
      </button>
    </div>
  );
}

Event Handling

React uses an HTML-like syntax to handle events:

function Bouton() {
  function handleClick(e) {
    e.preventDefault();
    console.log('Le bouton a été cliqué');
  }
  
  return (
    <button onClick={handleClick}>
      Cliquez ici
    </button>
  );
}

Conditional Rendering

You can display different elements depending on conditions:

function Connexion(props) {
  const estConnecte = props.estConnecte;
  
  return (
    <div>
      {estConnecte ? (
        <BoutonDeconnexion />
      ) : (
        <BoutonConnexion />
      )}
    </div>
  );
}

// Another approach
function Message(props) {
  const nonLu = props.messagesNonLus.length;
  
  return (
    <div>
      <h1>Bonjour !</h1>
      {nonLu > 0 && (
        <p>Vous avez {nonLu} message(s) non lu(s).</p>
      )}
    </div>
  );
}

Lists and Keys

To render lists of elements, use map() and unique keys:

function ListeArticles(props) {
  const articles = props.articles;
  
  return (
    <ul>
      {articles.map((article) => (
        <li key={article.id}>
          {article.titre}
        </li>
      ))}
    </ul>
  );
}

Advanced State Management

Context API

The Context API lets you share data between components without passing props down manually:

// Context creation
const ThemeContext = React.createContext('light');

// Context provider
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

// Consuming the context with useContext
function Bouton() {
  const theme = useContext(ThemeContext);
  
  return (
    <button className={theme}>
      Je suis stylisé selon le thème {theme}
    </button>
  );
}

useReducer for Complex State Management

useReducer is preferable to useState for complex state:

import { useReducer } from 'react';

// Reducer definition
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { compteur: state.compteur + 1 };
    case 'decrement':
      return { compteur: state.compteur - 1 };
    default:
      throw new Error();
  }
}

function Compteur() {
  // Using the reducer
  const [state, dispatch] = useReducer(reducer, { compteur: 0 });
  
  return (
    <div>
      Compteur: {state.compteur}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
}

State Management Libraries

For more complex applications, you can use dedicated libraries:

Redux

// Actions
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

// Action creators
function increment() {
  return { type: INCREMENT };
}

// Reducer
function compteurReducer(state = { valeur: 0 }, action) {
  switch (action.type) {
    case INCREMENT:
      return { valeur: state.valeur + 1 };
    case DECREMENT:
      return { valeur: state.valeur - 1 };
    default:
      return state;
  }
}

// Store
const store = createStore(compteurReducer);

// Connected component
function Compteur({ valeur, increment, decrement }) {
  return (
    <div>
      <span>{valeur}</span>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  );
}

const mapStateToProps = state => ({
  valeur: state.valeur
});

const mapDispatchToProps = {
  increment,
  decrement
};

export default connect(mapStateToProps, mapDispatchToProps)(Compteur);

Zustand (a Modern Alternative)

import create from 'zustand';

// Store creation
const useStore = create(set => ({
  compteur: 0,
  increment: () => set(state => ({ compteur: state.compteur + 1 })),
  decrement: () => set(state => ({ compteur: state.compteur - 1 })),
}));

// Usage in a component
function Compteur() {
  const { compteur, increment, decrement } = useStore();
  
  return (
    <div>
      <span>{compteur}</span>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  );
}

Routing with React Router

React Router lets you handle navigation in a React application:

import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Accueil</Link>
        <Link to="/a-propos">À propos</Link>
        <Link to="/contact">Contact</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<Accueil />} />
        <Route path="/a-propos" element={<APropos />} />
        <Route path="/contact" element={<Contact />} />
        <Route path="/produits/:id" element={<DetailProduit />} />
        <Route path="*" element={<PageNonTrouvee />} />
      </Routes>
    </BrowserRouter>
  );
}

// Accessing URL parameters
import { useParams } from 'react-router-dom';

function DetailProduit() {
  const { id } = useParams();
  
  return <h1>Détail du produit {id}</h1>;
}

Forms in React

Controlled Forms

In a controlled form, React manages the form state:

import { useState } from 'react';

function FormulaireConnexion() {
  const [email, setEmail] = useState('');
  const [motDePasse, setMotDePasse] = useState('');
  
  function handleSubmit(e) {
    e.preventDefault();
    console.log('Email:', email);
    console.log('Mot de passe:', motDePasse);
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>
          Email:
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </label>
      </div>
      <div>
        <label>
          Mot de passe:
          <input
            type="password"
            value={motDePasse}
            onChange={(e) => setMotDePasse(e.target.value)}
            required
          />
        </label>
      </div>
      <button type="submit">Se connecter</button>
    </form>
  );
}

Form Libraries

For complex forms, use libraries such as Formik or React Hook Form:

// With React Hook Form
import { useForm } from 'react-hook-form';

function FormulaireInscription() {
  const { register, handleSubmit, formState: { errors } } = useForm();
  
  const onSubmit = data => console.log(data);
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>Nom</label>
        <input {...register('nom', { required: 'Le nom est requis' })} />
        {errors.nom && <p>{errors.nom.message}</p>}
      </div>
      
      <div>
        <label>Email</label>
        <input 
          {...register('email', { 
            required: 'L\'email est requis',
            pattern: {
              value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
              message: 'Adresse email invalide'
            }
          })} 
        />
        {errors.email && <p>{errors.email.message}</p>}
      </div>
      
      <button type="submit">S'inscrire</button>
    </form>
  );
}

Performance Optimization

React.memo

React.memo lets you avoid unnecessary re-renders of function components:

const MonComposant = React.memo(function MonComposant(props) {
  // Your component
});

useMemo and useCallback

These hooks let you memoize values and functions:

import { useMemo, useCallback } from 'react';

function ExempleOptimisation({ a, b }) {
  // Memoizes the result of the computation
  const resultat = useMemo(() => {
    console.log('Calcul coûteux...');
    return a * b;
  }, [a, b]); // Recomputes only if a or b changes
  
  // Memoizes the function
  const handleClick = useCallback(() => {
    console.log('Bouton cliqué avec résultat:', resultat);
  }, [resultat]); // Recreated only if resultat changes
  
  return (
    <div>
      <p>Résultat: {resultat}</p>
      <button onClick={handleClick}>Cliquer</button>
    </div>
  );
}

Lazy Loading

Lazy loading lets you split your application into smaller chunks:

import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

// Lazy loading of components
const Accueil = lazy(() => import('./pages/Accueil'));
const APropos = lazy(() => import('./pages/APropos'));
const Contact = lazy(() => import('./pages/Contact'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>Chargement...</div>}>
        <Routes>
          <Route path="/" element={<Accueil />} />
          <Route path="/a-propos" element={<APropos />} />
          <Route path="/contact" element={<Contact />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

Testing in React

Unit Tests with Jest and React Testing Library

// Component to test
function Salutation({ nom }) {
  return <h1>Bonjour, {nom} !</h1>;
}

// Test
import { render, screen } from '@testing-library/react';
import Salutation from './Salutation';

test('affiche le message de salutation', () => {
  render(<Salutation nom="Alice" />);
  const element = screen.getByText(/bonjour, alice !/i);
  expect(element).toBeInTheDocument();
});

Interaction Tests

import { render, screen, fireEvent } from '@testing-library/react';
import Compteur from './Compteur';

test('incrémente le compteur lorsque le bouton est cliqué', () => {
  render(<Compteur />);
  
  // Check the initial value
  expect(screen.getByText(/compteur: 0/i)).toBeInTheDocument();
  
  // Click the button
  fireEvent.click(screen.getByText('+'));
  
  // Check the new value
  expect(screen.getByText(/compteur: 1/i)).toBeInTheDocument();
});

Deploying a React Application

Production Build

# With Create React App
npm run build

# With Vite
npm run build

Static Hosting

You can deploy your application to platforms such as:

  • Netlify: Drag and drop the build folder or set up continuous deployment
  • Vercel: Optimized for React applications, with automatic deployment from GitHub
  • GitHub Pages: Ideal for personal or open-source projects
  • Firebase Hosting: A complete solution with authentication and a database

Best Practices

  1. Structure your project: Organize your files by feature or by type
  2. Use function components: Simpler and more performant with Hooks
  3. Avoid prop drilling: Use Context or a state management library
  4. Write tests: Make sure your application works as expected
  5. Optimize performance: Use React.memo, useMemo, and useCallback judiciously
  6. Follow SOLID principles: Build components with a single responsibility
  7. Use TypeScript: Add static typing to prevent errors
  8. Document your components: Use tools like Storybook

Conclusion

React is a powerful library for building modern, high-performance user interfaces. Its flexibility and rich ecosystem make it an excellent choice for developing web applications of all sizes.

By mastering the core concepts and best practices covered in this article, you’ll be able to build React applications that are robust, maintainable, and scalable.

InSkillCoach

About InSkillCoach

Training and technology expert

Coach specializing in advanced technologies and AI, backed by GNeurone Inc.

Certifications:

  • AWS Certified Solutions Architect – Professional
  • Google Cloud certifications
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
920
105

Comments

Comments are powered by GitHub Discussions

Sign in with GitHub to join the discussion

Link copied!