Component definition

Component definition

options:

  • defining a component as a function
  • defining a component as a class (rather obsolete)

Component definition

component names always start with a capital letter

(to distinguish them from ordinary HTML elements)

Component definition

components will usually be defined as the default export in their own files

// TodoItem.tsx

export default function TodoItem() {
  // ...
}

Component defintion

components will often have an accompanying stylesheet file

// TodoItem.tsx

import './TodoItem.css';

Props and events

State and props

parent components can pass down data from their state to child components via props (unidirectional data flow)

if we know a component's props and state, we will know how it is rendered

Example: components and props in a todo app

image/svg+xml TodoItem TodoItem todo TodoList TodoApp todos AddTodo newTitle ... todos todo Statistics todos

Sharing state between components

if multiple components need to access the same state:

the state is stored in a component higher up in the component tree and passed down via props

(see: React docs: sharing state between components)

often, the main parts of the state will be stored in the top-level component (e.g. in App)

Events

child components can trigger events that cause the state in their parent component to update

Example: components and events in a todo app

image/svg+xml TodoItem TodoItem onDelete onCompletedChange TodoList TodoApp todos AddTodo newTitle ... Statistics onAdd onDelete onCompletedChange onDelete onCompletedChange

Component props

Component props

example:

<Rating value={prodRating} />

Component props

props are passed to function components via a props parameter:

type Props = { value: number };

function Rating(props: Props) {
  // we can access "props.value"
}

Component props

alternative notation with object destructuring:

type Props = { value: number };

function Rating({ value }: Props) {
  // we can access "value" directly
}

Component props

implementation of a Rating component:

type Props = { value: number };

function Rating({ value }: Props) {
  const starIds = [1, 2, 3, 4, 5];
  return (
    <div className="Rating">
      {starIds.map((id) => (
        <span key={id}>{id <= value ? '★' : '☆'}</span>
      ))}
    </div>
  );
}

Component props

exercise: create a progress bar component:

<ProgressBar percentage={75} color="lightgreen" />

Passing content to components

Passing content to components

example with one "slot":

<Notification type="error">
  <h1>Error</h1>
  <p>Changes could not be saved</p>
</Notification>

Passing content to components

example with two named "slots":

<Notification
  type="error"
  header={<h1>Error</h1>}
  body={<p>Changes could not be saved</p>}
/>

Passing content to components

equivalent notations:

<Notification type="error">
  <div>foo</div>
</Notification>
<Notification type="error" children={<div>foo</div>} />

anything passed in between opening and closing tags will be received as props.children

Passing content to components

implementation of a Notification component:

type Props = {
  type: 'info' | 'warning' | 'error';
  children: ReactNode;
};

function Notification({ type, children }: Props) {
  return (
    <div className={`Notification Notification--${type}`}>
      {children}
    </div>
  );
}

Passing content to components

Exercise: Card component with Styling (example result with CSS declarations)

<Card>
  <h1>heading</h1>
  <p>content</p>
</Card>

Exercises

Exercises

Task: In the todo application, extract a Statistics component that displays information like this:

5 todos (3 incomplete, 2 completed)

potential simple file structure:

  • index.tsx
  • App.tsx
  • App.css
  • components
    • Statistics.tsx
    • Statistics.css
    • ...

Exercises

Task: Create simple reusable components that are mainly used for styling

examples:

These components should not provide any interactivity (have no events)

Component events

Component events

Props:

A component (e.g. App) may pass data (from its state) down to a child component (e.g. Rating)

Events:

A sub-component may trigger an event which will cause the state of a parent to update

Component events

Event handlers are defined as functions and passed down via props

Event names conventionally start with on, e.g. onChange, onClose, ...

Component events

Example:

<Rating
  value={prodRating}
  onChange={(newRating) => setProdRating(newRating)}
/>

Component events

example property types for a rating component:

type Props = {
  value: number;
  onChange?: (value: number) => void;
};

Component events

function Rating({ value, onChange }: Props) {
  const starIds = [1, 2, 3, 4, 5];
  return (
    <div className="Rating">
      {starIds.map((id) => (
        <span
          onClick={() => {
            if (onChange) {
              onChange(id);
            }
          }}
          key={id}
        >
          {id <= value ? '★' : '☆'}
        </span>
      ))}
    </div>
  );
}

Component events

Using the Rating component:

const [prodRating, setProdRating] = useState(3);
<Rating
  value={prodRating}
  onChange={(newRating) => setProdRating(newRating)}
/>

Exercises

Exercises

Task: Create reusable interactive components (with some styling)

examples:

Exercises

Task: split the todo app into smaller components that communicate via props and events (e.g. AddTodo, TodoItem, TodoList)

Exercises

Task: draft the structure of props, event and state for various components (e.g. Calendar, ColorPicker, BarChart, Tabs)