React - Work In Progress

react

React Component Essentials

  • Definition: React’s fundamental building block.
  • Characteristics:
    • Combines UI logic and appearance.
    • Ranges from small (e.g., a button) to large (e.g., a page).
    • Essentially JS functions returning UI markup.
// JSX

function MyButton() {
    return (
        <button>
            Click Me!
        </button>
    );
}

After declaring this button component, we can nest it inside of another component.

// JSX

export default function MyApp() {
    return (
        <div>
            <h1>Welcome to my app!</h1>
            <MyButton />
        </div>
    );
}
React ComponentHTML Tag
Appearance<MyButton /><button>
first letterCapitalizedLowercase

JSX in React

  • Nature: Syntax extension for JavaScript.
  • Features:
    • Mixes markup with JS.
    • Compiled to JS, resembling HTML.
    • Requires closed tags and a single root element.
  • Tooling: Supported by standard React tools.
  • HTML to JSX converter

React Component vs. JSX Tags

React ComponentJSX Tags
Combines JSX, logic, appearanceRepresents DOM elements in JSX syntax

Styling React Components

Use className for CSS classes, not class.

// JSX

<img className="avatar" />
/* CSS */

.avatar {
    border-radius: 50%;
}

Adding CSS: Use <link> in HTML or consult documentation for build tools/frameworks.

Displaying Data with JSX

  • Integration: JSX seamlessly combines JavaScript and markup.
  • Mechanism: Use curly braces {} to embed JavaScript within JSX.
  • Usage: Embed variables, expressions, and even entire components.

Embedding Variables

Embed JavaScript variables directly into JSX to display dynamic data.

// JSX

function UserGreeting() {
  const user = { name: 'Alice' };

  return (
    <h1>Welcome, {user.name}!</h1>
  );
}

Embedding Expressions

Expressions can be used for more dynamic content.

// JSX

function UserGreeting() {
  const user = { name: 'Bob', age: 30 };

  return (
    <h1>
      {user.name} is {user.age} years old.
    </h1>
  );
}

Dynamic Attributes

Use curly braces to dynamically set attributes like className and src.

// JSX

function UserProfile() {
  const user = {
    imageUrl: 'path/to/image.jpg',
    altText: 'User Profile Picture'
  };

  return (
    <img src={user.imageUrl} alt={user.altText} />
  );
}

Conditional Rendering in JSX

  • Approach: Utilize JavaScript’s conditional logic within JSX.
  • Techniques: if statements, ternary operators, logical operators.

Using if Statement

// JSX

function WelcomeMessage({ isLoggedIn }) {
  let message;

  if (isLoggedIn) {
    message = <span>Welcome back!</span>;
  } else {
    message = <span>Please sign in.</span>;
  }

  return message;
}

Ternary Operator

A concise way to render components conditionally.

// JSX

function WelcomeMessage({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? <span>Welcome back!</span> : <span>Please sign in.</span>}
    </div>
  );
}

Logical Operator (&&)

Useful for rendering something only if a condition is true.

// JSX

function Alert({ show, message }) {
  return (
    <div>
      {show && <div className="alert">{message}</div>}
    </div>
  );
}

Rendering Lists in React

  • Key Concept: Use JavaScript’s array methods like .map() to render lists.
  • Key Prop: Use key prop for list items for efficient rendering.

Example: Rendering an Array of Items

// JSX

function FruitList() {
  const fruits = ['Apple', 'Banana', 'Cherry'];

  return (
    <ul>
      {fruits.map((fruit, index) => <li key={index}>{fruit}</li>)}
    </ul>
  );
}
  • Note: Always provide a unique key prop to each list item to help React identify which items have changed, added, or removed.

Handling Events in React

  • Overview: React allows you to handle events similar to handling events in regular JavaScript but with a JSX syntax twist.

Basic Event Handling

Inline functions or methods are commonly used as event handlers in React.

// JSX

function ActionButton() {
  const handleClick = () => {
    alert('Button clicked!');
  };

  return (
    <button onClick={handleClick}>Click Me</button>
  );
}

Notice: The onClick attribute in JSX takes a function (here, handleClick) as its value, which gets executed when the event occurs.

Passing Arguments to Event Handlers

You can pass additional data to event handlers using arrow functions.

// JSX

function ItemList({ items }) {
  const handleItemClick = (item) => {
    alert(`You selected ${item}`);
  };

  return (
    <ul>
      {items.map(item => 
        <li key={item} onClick={() => handleItemClick(item)}>{item}</li>
      )}
    </ul>
  );
}

Notice: Each list item (<li>) has an onClick event that calls handleItemClick with the current item as an argument, demonstrating how to pass additional data to the handler.

Updating the UI with State

  • Concept: State in React allows you to manage data that changes over time and automatically re-render the component when the data changes.

Using useState to Track State

The useState hook is used to add state to a functional component.

// JSX

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

Notice: useState initializes the count state variable. The setCount function is used to update count, and the component re-renders each time the state changes.

State and Event Handling Together

Combining state with event handlers enables interactive UIs.

// JSX

function TextInput() {
  const [text, setText] = useState('');

  const handleChange = (event) => {
    setText(event.target.value);
  };

  return (
    <input type="text" value={text} onChange={handleChange} />
  );
}

Notice: The text state is updated with every keystroke in the input field, showcasing a real-time update of the state based on user input.

Propagating State Changes

  • Lifting State Up: Moving state to a common ancestor component allows different components to share and update the same state.

Example: Sharing State Between Components

// JSX

import { useState } from 'react';

function ParentComponent() {
  const [sharedData, setSharedData] = useState('Initial Value');

  return (
    <>
      <ChildComponentA data={sharedData} />
      <ChildComponentB updateData={setSharedData} />
    </>
  );
}

function ChildComponentA({ data }) {
  return <p>Data from parent: {data}</p>;
}

function ChildComponentB({ updateData }) {
  return (
    <button onClick={() => updateData('Updated Value')}>
      Update Data
    </button>
  );
}

Notice: ParentComponent holds the state (sharedData) and passes it to ChildComponentA and ChildComponentB. ChildComponentB can update sharedData via updateData, demonstrating the concept of lifting state up and sharing it across components.

Handling Side Effects with useEffect

  • Overview: useEffect is used for side effects in functional components, like data fetching, subscriptions, or manually changing the DOM.

Basic Usage of useEffect

useEffect runs after every render by default.

// JSX

import { useEffect } from 'react';

function UserComponent({ userId }) {
  useEffect(() => {
    // Fetch user data
    fetchData(userId);
  });

  // Component render logic
}

Notice: The useEffect hook is used to perform side effects, such as fetching data based on userId. It runs after every render of UserComponent.

Conditional Execution in useEffect

Run useEffect only when certain values change by passing them in an array.

// JSX

useEffect(() => {
  // Fetch user data
  fetchData(userId);
}, [userId]); // Only re-run if userId changes

Notice: useEffect will only re-run when userId changes, optimizing performance by avoiding unnecessary data fetching on every render.

Optimizing Performance in React

  • Concept: React provides several methods to optimize the performance of applications, such as React.memo, useCallback, and useMemo.

Using React.memo for Component Memoization

React.memo is a higher-order component that memoizes your component.

// JSX

import React from 'react';

const MyComponent = React.memo(function MyComponent(props) {
  // Component logic
});

Notice: React.memo prevents unnecessary re-renders of MyComponent if its props haven’t changed, enhancing performance, especially in large and complex component trees.

useCallback for Memoizing Functions

useCallback returns a memoized version of the callback that only changes if one of the dependencies has changed.

// JSX

import { useCallback } from 'react';

function MyComponent() {
  const memoizedCallback = useCallback(
    () => {
      // Function logic
    },
    [], // Dependencies
  );

  // Component logic
}

Notice: useCallback is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.

Using Context for State Management

  • Overview: The Context API allows you to manage and share state across multiple components without prop drilling.

Creating a Context

Define a Context for your state.

// JSX

import React, { createContext, useState } from 'react';

const MyContext = createContext();

function MyApp() {
  const [sharedState, setSharedState] = useState('initial value');

  return (
    <MyContext.Provider value={{ sharedState, setSharedState }}>
      {/* Rest of your app */}
    </MyContext.Provider>
  );
}

Notice: MyContext.Provider wraps your app components, allowing any child component to access and manipulate sharedState.

Consuming Context in a Component

Use the useContext hook to access context in a component.

// JSX

import React, { useContext } from 'react';
import MyContext from './MyContext';

function MyComponent() {
  const { sharedState, setSharedState } = useContext(MyContext);

  // Component logic using sharedState
}

Notice: useContext(MyContext) provides access to sharedState and setSharedState, enabling the component to interact with the shared state without prop drilling.