Full-stack Web Technologies

CHAPTER 3
React Components

A component is just a function that will render some data in JSX.

We saw a function rendering a User in the last chapter:

function renderUser(name: string, age: number) {
  return (
    <div>
      <span>{name}</span>
      <span>{age}</span>
    </div>
  );
}

In past versions of React (still today) components could also be classes, but this style is not the dominant anymore, even if a lot of code is still written in that style

Props

But there is very important convention: function components can receive only one parameter, which is an object.

type UserProps = {
  name: string;
  age: numger;
};
const User = (props: UserProps) => (
  <div className="user">
    <span className="name">{props.name}</span>
    <span className="age">{props.age}</span>
  </div>
);

Instead of passing data as multiple parameters, all data is passed as a single object, called props. In a sense, these object represents the "properties" of the component.

When using a props object you can destructure the fields of the object before-hand, such as not to carry the prefix props. in the code inside the component.

type UserProps = {
  name: string;
  age: numger;
};
const User = ({ name, age }: UserProps) => (
  <div className="user">
    <span className="name">{name}</span>
    <span className="age">{age}</span>
  </div>
);

Functional Style

The props of a component are its input data, and therefore, from a programming perspective, they are input paramenters, which are read-only. A React component is, therefore, a pure function, one that will only depend on the values of the props, and keeps nothing or uses nothing else. This is the only way in which React can rest assured that its job will be effective. If components weren't pure the job of React would be much more difficult.

Calling Components

To use a component, you can call the function directly, of course:

<p>{User({ name: "johndoe", age: 27 })}</p>

but here is where JSX can make things a lot better and easier to read.

Instead of calling the function directly, in React you can just call it by pretending that an element exists with that name, and the attributes are the fields in the props object:

<p>
  <User name="johndoe" age={27} />
</p>

The <User ... /> element will transform into a call to the User function, and that is what gives us Element Abstraction, i.e., the ability to define ever more complex elements to solve problems at the highest possible level.

An important rule about components:

  • They have to start in UpperCase. To distinguish HTML elements from React components, JSX just uses the name of the function: if it starts in upper-case it is a component (User, Card, MyButton, Movie), otherwise it is a normal HTML element (div, span, p, em, article, header, ...)

Children

If the component has an opening tag and a closing tag, such as in:

<BounceEffect>{/* ... */}</BounceEffect>

then the elements inside of it are its children. The props, therefore, will contain a children field with an array of the children, which we can

const BounceEffect = ({ children }) => (
  <div className="outer">
    <div className="inner">{children}</div>
  </div>
);

Components as files

Typically, unless we create tiny components, the most common way to develop React components is to put each component in a separated file. So our User component would be in file User.tsx (notice that the filename is in uppercase):

// User.tsx

type UserProps = {
  name: string;
  age: numger;
};
export default function User (props: UserProps) (
  <div className="user">
    <span className="name">{props.name}</span>
    <span className="age">{props.age}</span>
  </div>
);

To be able to export default the component and name it at the same time(although it would not be strictly necessary) it is useful to adopt the function notation since arrow functions would not allow naming or would need to do the export default separately.