Week 8: React II

INFO 253A: Front-end Web Architecture

Kay Ashaolu

React Chapter 4:

Forms, Validation, and Simple Animation

Introduction to Forms in React

  • Forms are essential for user interaction in web applications.
  • Handling forms in React involves managing state and event handlers.

Managing Form Input and State

  • Use the useState hook to manage form input state.
  • Example:
 const [text, setText] = useState('');
  • Connect form inputs to state using value and onChange props.
  • Reflects user input in the component's state.

Example: Creating the FeedbackForm Component

  • Create a new component: FeedbackForm.jsx.
  • Use the shared Card component to wrap the form for consistent styling.
  • Structure:
 <Card>
   <form>
     {/* Form elements go here */}
   </form>
 </Card>

Using Custom Components

  • Reuse components like Card and Button to maintain consistency.
  • Create a Button component with customizable props.
  • Example Button Component Usage:
 <Button type="submit" version="primary">
   Send
 </Button>

Creating a Custom Button Component

  • Allows for reusable and customizable buttons across the app.
  • Accepts props like type, version, isDisabled.
  • Button Component Implementation:
 const Button = ({ children, version, type, isDisabled }) => {
   return (
     <button type={type} disabled={isDisabled} className={`btn btn-${version}`}>
       {children}
     </button>
   );
 };

Props and Default Props in Components

  • Define default prop values using defaultProps.
  • Ensures components have default behavior if no props are passed.
  • Example:
 Button.defaultProps = {
   version: 'primary',
   type: 'button',
   isDisabled: false,
 };

Adding PropTypes for Type Checking

  • Use prop-types library for runtime prop type checking.
  • Helps catch bugs by ensuring components receive correct prop types.
  • Example:
 import PropTypes from 'prop-types';

 Button.propTypes = {
   children: PropTypes.node.isRequired,
   version: PropTypes.string,
   type: PropTypes.string,
   isDisabled: PropTypes.bool,
 };

Implementing Real-Time Form Validation

  • Disable submit button until input meets criteria (e.g., at least 10 characters).
  • Use state to manage button disabled state and validation messages.
  • Example:
 const [btnDisabled, setBtnDisabled] = useState(true);
 const [message, setMessage] = useState('');

 const handleTextChange = (e) => {
   if (text.trim().length <= 10) {
     setBtnDisabled(true);
     setMessage('Text must be at least 10 characters');
   } else {
     setBtnDisabled(false);
     setMessage('');
   }
   setText(e.target.value);
 };

Handling Form Submission

  • Add onSubmit handler to the form element.
  • Prevent default browser behavior with e.preventDefault().
  • Collect form data and update state or communicate with parent components.

Conditional Rendering Based on Form State

  • Show validation messages only when necessary.
  • Use conditional rendering in JSX.
  • Example:
 {message && <div className="message">{message}</div>}
  • The message appears only if message is not empty.

Example: Creating the RatingSelect Component

  • Component for selecting a rating (1-10).
  • Uses styled radio buttons for user selection.
  • Manages selected rating with its own state.
 const [selected, setSelected] = useState(10);

 const handleChange = (e) => {
   setSelected(+e.currentTarget.value);
 };

Lifting State Up in React

  • Share state between components by lifting it up to a common ancestor.
  • Pass functions as props to child components to update parent state.
  • Example in FeedbackForm.jsx:
 <RatingSelect select={(rating) => setRating(rating)} />
  • In RatingSelect.jsx, call select(selected) to update parent state.

Adding New Feedback Items to State

  • Collect input data and create a new feedback object.
  • Use useState to manage the list of feedback items.
  • Example:
 const addFeedback = (newFeedback) => {
   setFeedback([newFeedback, ...feedback]);
 };

Using UUIDs for Unique IDs

  • Install uuid library to generate unique IDs for new items.
  • Ensures each feedback item has a unique identifier.
  • Example:
 import { v4 as uuidv4 } from 'uuid';

 const newFeedback = {
   id: uuidv4(),
   text,
   rating,
 };

Updating Global State in React

  • Use state setters to update state immutably.
  • Avoid direct mutation of state for predictability and performance.
  • Example:
 setFeedback([newFeedback, ...feedback]);
  • Prepends the new feedback to the existing list.

Recap and Key Takeaways

  • Forms in React require careful state management.
  • Custom components promote code reusability and maintainability.
  • Real-time validation improves user experience.
  • Managing global state allows for dynamic and interactive applications.

React Chapter 5: Creating Routes and Links

The Role of Routing in Web Applications

  • Routing allows navigation between different views without full page reloads
  • Essential for Single Page Applications (SPAs)
  • Maintains browser history and enables deep linking
  • Enhances user experience with faster navigation

React and Client-Side Routing

  • React focuses on building UI components
  • Does not include built-in routing
  • react-router-dom is the de facto standard for routing in React apps
  • Provides components and hooks for declarative routing

Installing react-router-dom

npm install react-router-dom
  • Install via npm to add routing capabilities
  • Includes components like BrowserRouter, Routes, Route, Link, etc.

Creating Links with the Link Component

  • Use Link instead of <a> for internal navigation
import { Link } from 'react-router-dom';

function Navbar() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
    </nav>
  );
}
  • Prevents full page reloads
  • Enables client-side routing and preserves application state

Using NavLink for Active Links

  • NavLink applies styling to the active link
import { NavLink } from 'react-router-dom';

function Navbar() {
  return (
    <nav>
      <NavLink to="/" className={({ isActive }) => (isActive ? 'active' : '')}>
        Home
      </NavLink>
      <NavLink to="/about" className={({ isActive }) => (isActive ? 'active' : '')}>
        About
      </NavLink>
    </nav>
  );
}
  • Customize active styles using className or style props
  • Helps users identify the current page

Dynamic Routes with URL Parameters

  • Define routes with dynamic parameters
<Route path="/post/:id" element={<Post />} />
  • Access parameters using useParams hook
import { useParams } from 'react-router-dom';

function Post() {
  const { id } = useParams();
  return <h1>Post ID: {id}</h1>;
}
  • Enables dynamic content based on the URL
  • Commonly used for user profiles, articles, etc.

Programmatic Navigation with useNavigate

  • Navigate in response to events or logic
import { useNavigate } from 'react-router-dom';

function ContactForm() {
  const navigate = useNavigate();

  const handleSubmit = () => {
    // Submit form logic
    navigate('/thank-you');
  };

  return <button onClick={handleSubmit}>Submit</button>;
}
  • Facilitates redirects after actions like form submissions

Nested Routes and the Outlet Component

  • Define nested routes for complex layouts
import { Outlet } from 'react-router-dom';

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Outlet />
    </div>
  );
}

Nested Routes and the Outlet Component

  • Use Outlet to render child routes within parent components
<Routes>
  <Route path="/dashboard" element={<Dashboard />}>
    <Route path="stats" element={<Stats />} />
    <Route path="settings" element={<Settings />} />
  </Route>
</Routes>
  • Organizes routes hierarchically
  • Enhances code readability and maintainability

Summary and Best Practices

  • Routing is integral to SPA architecture
  • React Router enhances navigation without page reloads
  • Always use Link or NavLink for internal links
  • Utilize hooks like useParams and useNavigate for dynamic routing
  • Keep routes organized and consider nested routes for complex apps

React Chapter 6:

Context API, useContext Hook, and Deployment

Introduction to Context API

  • Problem: Prop drilling makes state management cumbersome in large applications.
  • Solution: Context API allows passing data through the component tree without manually passing props at every level.
  • Benefit: Simplifies state management and makes code cleaner and more maintainable.

Prop Drilling Issue

  • Passing props through multiple nested components becomes messy.
  • Difficult to manage in large applications with deep component hierarchies.
  • Example of prop drilling in App.js:
 <FeedbackList feedback={feedback} handleDelete={deleteFeedback} />

Creating Context and Provider

  • Step 1: Import createContext and useState from React.
 import { createContext, useState } from 'react';
  • Step 2: Create a new context.
 const FeedbackContext = createContext();
  • Step 3: Create a provider component.
 export const FeedbackProvider = ({ children }) => {
   // State and functions here
   return (
     <FeedbackContext.Provider value={{ /* values */ }}>
       {children}
     </FeedbackContext.Provider>
   );
 };

Setting Up FeedbackContext.js

  • Initialize state within the provider.
 const [feedback, setFeedback] = useState([
   { id: 1, text: 'This item is from context', rating: 10 },
 ]);
  • Provide state and functions via the value prop.
 <FeedbackContext.Provider value={{ feedback }}>
   {children}
 </FeedbackContext.Provider>

Wrapping App with FeedbackProvider

  • Import FeedbackProvider in App.js.
 import { FeedbackProvider } from './context/FeedbackContext';
  • Wrap the entire application.
 return (
   <FeedbackProvider>
     {/* Your components */}
   </FeedbackProvider>
 );

Consuming Context with useContext

  • Import useContext and the context.
 import { useContext } from 'react';
 import FeedbackContext from '../context/FeedbackContext';
  • Use the context within a component.
 const { feedback } = useContext(FeedbackContext);

Updating Components to Use Context

  • Remove props drilling by accessing context directly.
  • Example in FeedbackList.js:
 const { feedback } = useContext(FeedbackContext);
  • No longer need to pass feedback as a prop from App.js.

Moving Functions to Context

  • Move state manipulation functions to context.
 const deleteFeedback = (id) => {
   setFeedback(feedback.filter((item) => item.id !== id));
 };
  • Provide functions via the value prop.
 <FeedbackContext.Provider value={{ feedback, deleteFeedback }}>
   {children}
 </FeedbackContext.Provider>

Updating the Form and List Components

  • Access functions from context using useContext.
 const { deleteFeedback } = useContext(FeedbackContext);
  • Remove function props from components and use context functions instead.
  • Simplifies component interfaces and reduces prop drilling.

Implementing Edit Feedback Feature

  • Add an edit icon to FeedbackItem.
 <button onClick={() => editFeedback(item)}>
   <FaEdit color='purple' />
 </button>
  • Create editFeedback function in context to handle the edit state.
 const [feedbackEdit, setFeedbackEdit] = useState({
   item: {},
   edit: false,
 });

 const editFeedback = (item) => {
   setFeedbackEdit({
     item,
     edit: true,
   });
 };

Managing Side Effects with useEffect

  • Import and use useEffect to handle side effects.
 import { useEffect } from 'react';

 useEffect(() => {
   if (feedbackEdit.edit === true) {
     // Update form state
   }
 }, [feedbackEdit]);
  • Update form fields when the edit state changes.

Updating Feedback Item

  • Add updateFeedback function in context.
 const updateFeedback = (id, updItem) => {
   setFeedback(
     feedback.map((item) => (item.id === id ? { ...item, ...updItem } : item))
   );
 };
  • Modify FeedbackForm to handle updates.
 if (feedbackEdit.edit === true) {
   updateFeedback(feedbackEdit.item.id, newFeedback);
 } else {
   addFeedback(newFeedback);
 }

Deploying to Netlify

  • Create a GitHub repository and push your code.
  • Sign up for Netlify and create a new site from Git.
  • Select your repository and configure build settings:
    • Build Command: npm run build
    • Publish Directory: build
  • Deploy the site and access it via the provided URL.

Alternative Deployment Options

  • Vercel: Easy deployment with Git integration.
  • AWS Amplify: Scalable hosting solution.
  • GitHub Pages: Suitable for static sites.
  • Heroku: Can be used with a server-side component.

Conclusion and Next Steps

  • Context API simplifies state management in React apps.
  • useContext hook allows consuming context easily.
  • Deployment is straightforward with platforms like Netlify.