Codecademy Logo

Building Interactive React Components Using AI

Related learning

  • Learn front-end development with AI tools. This course teaches you to build React applications using AI coding agents to speed up workflows.
    • Includes 2 Courses
    • With Certificate
    • Intermediate.
      6 hours

The State Hook

The useState() Hook lets you add React state to function components. It should be called at the top level of a React function definition to manage its state.

initialState is an optional value that can be used to set the value of currentState for the first render. The stateSetter function is used to update the value of currentState and rerender our component with the next state value.

const [currentState, stateSetter] = useState(initialState);

JSX element event listeners

In JSX, event listeners are specified as attributes on elements. An event listener attribute’s name should be written in camelCase, such as onClick for an onclick event, and onMouseOver for an onmouseover event.

An event listener attribute’s value should be a function. Event listener functions can be declared inline or as variables and they can optionally take one argument representing the event.

// Basic example
const handleClick = () => alert("Hello world!");
const button = <button onClick={handleClick}>Click here</button>;
// Example with event parameter
const handleMouseOver = (event) => event.target.style.color = 'purple';
const button2 = <div onMouseOver={handleMouseOver}>Drag here to change color</div>;

Event Handlers and State in React

Event handler functions in React are often used to update state. These handler functions often receive an event as an argument, which is used to update state values correctly.

In the example code, we use event.target.value to get the input’s value.

function MyComponent() {
const [ text, setText ] = useState("");
const handleChange = (event) => {
setText(event.target.value);
}
return (
<div>
<input onChange={handleChange} value={text} />
<p>You typed {text}</p>
</div>
);
}

State Setter Callback Function

When the previous state value is used to calculate the next state value, pass a function to the state setter. This function accepts the previous value as an argument and returns an updated value.

If the previous state is not used to compute the next state, just pass the next state value as the argument for the state setter.

Note: The Reset button doesn’t need the function form since it’s not based on the previous state; it’s setting a fixed value.

function Counter({ initialCount }) {
const [count, setCount] = useState(initialCount);
return (
<div>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
<button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
</div>
);
}

JSX conditionals

JSX does not support if/else syntax in embedded JavaScript. There are three ways to express conditionals for use with JSX elements:

  1. a ternary within curly braces in JSX
  2. an if statement outside a JSX element, or
  3. the && operator.
// Using ternary operator
const headline = (
<h1>
{ age >= drinkingAge ? 'Buy Drink' : 'Do Teen Stuff' }
</h1>
);
// Using if/else outside of JSX
let text;
if (age >= drinkingAge) { text = 'Buy Drink' }
else { text = 'Do Teen Stuff' }
const headline = <h1>{ text }</h1>
// Using && operator. Renders as empty div if length is 0
const unreadMessages = [ 'hello?', 'remember me!'];
const update = (
<div>
{unreadMessages.length > 0 &&
<h1>
You have {unreadMessages.length} unread messages.
</h1>
}
</div>
);

JSX and conditionals

In JSX, && is commonly used to render an element based on a boolean condition. && works best in conditionals that will sometimes do an action, but other times do nothing at all.

If the expression on the left of the && evaluates as true, then the JSX on the right of the && will be rendered. If the first expression is false, however, then the JSX to the right of the && will be ignored and not rendered.

// If baby is false and age is above 25,
// all list items will display.
// Otherwise, only items meeting their conditions will show.
const tasty = (
<ul>
<li>Applesauce</li>
{ !baby && <li>Pizza</li> }
{ age > 15 && <li>Brussels Sprouts</li> }
{ age > 20 && <li>Oysters</li> }
{ age > 25 && <li>Grappa</li> }
</ul>
);

JSX .map() method

The array method map() comes up often in React. It’s good to get in the habit of using it alongside JSX.

If you want to create a list of JSX elements from a given array, then map() over each element in the array, returning a list item for each one.

const strings = ['Home', 'Shop', 'About Me'];
const listItems = strings.map(string => <li>{string}</li>);
<ul>{listItems}</ul>

props

Components can pass information to other components. When one component passes information to another, it is passed as props through one or more attributes.

The example code demonstrates the use of attributes in props. SpaceShip is the component and ride is the attribute. The SpaceShip component will receive ride in its props.

<SpaceShip ride="Millennium Falcon" />

React State Setter

Invoking a state setter function causes the component to re-render. This triggers React to execute the component function, updating the display with the new state value. This mechanism helps keep components responsive to changes, such as user interactions or external data updates.

The example shows how clicking a button triggers a re-render. Notice that the timestamp updates on each render, proving the component function runs again.

import React, { useState } from 'react';
function UserProfile() {
const [name, setName] = useState('Guest');
const [theme, setTheme] = useState('light');
// This gets a new timestamp every time the component renders
const renderTime = new Date().toLocaleTimeString();
return (
<div>
<h2>Welcome, {name}!</h2>
<p>Last rendered at: {renderTime}</p>
<button onClick={() => setName('Alex')}>
Change Name
</button>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</div>
);
}

React State Flow

Parent components share state setter functions with children as props. This allows children to update parent state indirectly, preserving React’s unidirectional data flow. For example, a form child component can send user input to update a parent state. This is common when state management is crucial in applications with nested components.

// In ParentComponent.jsx
const ParentComponent = () => {
const [message, setMessage] = useState('Hello');
return (
<div>
<h1>{message}</h1>
<ChildComponent setParentMessage={setMessage} />
</div>
);
};
// In ChildComponent.jsx
const ChildComponent = ({ setParentMessage }) => {
return (
<button onClick={() => setParentMessage('Hello from Child!')}>
Update Parent Message
</button>
);
};

React State Management

In React component hierarchies, state is ideally placed in the lowest common ancestor that requires access to the data. If only one component needs the data, keep the state in that component. For shared data among sibling components, lift the state to their shared parent. This approach ensures efficient state management, minimizes redundant state copies, and facilitates easier maintenance. Consider using this strategy when managing dynamic data or user interactions across components.

In this example, the two children would each need their own data state, which results in duplication. After lifting, there is a single parent state that flows down to both children.

// State inside a single component
const SingleComponent = () => {
const [data, setData] = useState('Single Component State');
return <div>{data}</div>;
};
// Lifted state example
const SharedParent = () => {
const [sharedData, setSharedData] = useState('Shared State');
return (
<div>
<ChildComponent1 data={sharedData} />
<ChildComponent2 data={sharedData} />
</div>
);
};
const ChildComponent1 = ({ data }) => <div>Component 1: {data}</div>;
const ChildComponent2 = ({ data }) => <div>Component 2: {data}</div>;

React State Memory

State values persist across component re-renders while regular variables reset to their initial values. Use state to store data like user inputs or results fetched from APIs since it ensures that information is retained even if the UI updates. This is crucial for dynamic applications that require responsiveness to user interactions.

import React, { useState } from 'react';
function Counter() {
// Declare a state variable 'count', initialized to 0
const [count, setCount] = useState(0);
// Increments count by 1 on button click
function increment() {
setCount(prev => prev + 1);
}
return (
<div>
<p>Current Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}

Why Hooks?

Hooks are functions that let us “hook into” state and lifecycle functionality in function components.

Hooks allow us to:

  • reuse stateful logic between components
  • simplify and organize our code to separate concerns, rather allowing unrelated data to get tangled up together
  • avoid confusion around the behavior of the this keyword
  • avoid class constructors, binding methods, and related advanced JavaScript techniques

Rules for Using Hooks

There are two main rules to keep in mind when using hooks:

  1. Only call hooks from React function components.
  2. Only call hooks at the top level, to be sure that hooks are called in the same order each time a component renders.

Common mistakes to avoid are calling hooks inside of loops, conditions, or nested functions.

// Instead of confusing React with code like this:
if (userName !== '') {
useEffect(() => {
localStorage.setItem('savedUserName', userName);
});
}
// We can accomplish the same goal, while consistently calling our hook every time:
useEffect(() => {
if (userName !== '') {
localStorage.setItem('savedUserName', userName);
}
});

Returning HTML Elements and Components

A function component can return any JSX, including a mix of HTML elements and custom React components.

In the example, we return a <Logo /> component and a “vanilla” HTML title.

This assumes that <Logo /> is defined elsewhere.

function Header() {
return (
<div>
<Logo />
<h1>Codecademy</h1>
</div>
);
}

Changing Props and State

In React, a component should never change its own props directly. A parent component should change them.

State, on the other hand, is the opposite of props: a component keeps track of its own state and can change it at any time.

The example code shows a component that accepts a prop, subtitle, which never changes. It also has a state object which does change.

function Clock(props) {
const [ date, setDate ] = useState(new Date());
const updateTime = () => {
setDate(new Date());
}
return (
<div>
<h1>It is currently {date.toLocaleTimeString()}</h1>
<h2>{props.subtitle}</h2>
<button onClick={updateTime}>Update the clock</button>
</div>
);
}

React Component Props

Each React component can receive data via props, which are accessed through the props object. This allows components to be dynamic, utilizing data passed through JSX. If the parent component doesn’t provide these props, the component can use default values. This feature is valuable in building reusable UI components, handling customization and flexibility in rendering.

// Reusable Greeting component
function Greeting(props) {
const { name, message } = props;
return (
<div>
<h2>Hello, {name}!</h2>
<p>{message}</p>
</div>
);
}
// Default values if props are not provided
Greeting.defaultProps = {
name: 'Guest',
message: 'Welcome to our site.',
};
// Parent component demonstrating usage
function App() {
return (
<>
{/* Full props */}
<Greeting name="Alice" message="Great to see you!" />
{/* Partial props (uses default message) */}
<Greeting name="Bob" />
{/* No props (uses all defaults) */}
<Greeting />
</>
);
}
export default App;

Learn more on Codecademy

  • Learn front-end development with AI tools. This course teaches you to build React applications using AI coding agents to speed up workflows.
    • Includes 2 Courses
    • With Certificate
    • Intermediate.
      6 hours