Skip to main content

Component

Function and Class Components

The easiest way to define a component is to write a JavaScript function. The component can also be written as an ES6 class and extends the class Component from Costro. This offers more features which you will discover below. These can be imported from other files.

src/components/home.js
function Home(props) {
return <h2>Hello, {props.name}</h2>;
}

Transform a Function to a Class

You can transform a function component to a class component with the following steps:

  1. Create an ES6 class with the same name, that extends Component.
  2. Add a single method render().
  3. Move the content of the function into the render() method.
  4. Replace props with this.props in the render() content.

Lifecycle hooks

Lifecycle hooks are available on Class Components only.

We can declare special methods on the component class to run some code when a component is rendered or destroyed.

Try it on CodeSandbox.

Before render

The beforeRender() method runs before the component output has been rendered to the DOM.

src/components/home.js
class Home extends Component {
beforeRender() {
// The component is not yet rendered to the DOM
}

render() {
return <h2>Home</h2>;
}
}
tip

The beforeRender() method can be asynchronous to get data before rendering the component. This will delay the execution of the render method.

After render

The afterRender() method runs after the component output has been rendered to the DOM.

src/components/home.js
class Home extends Component {
render() {
return <h2>Home</h2>;
}

afterRender() {
// The component is rendered to the DOM
}
}
tip

Access to the component's DOM must be done during the afterRender hook.

Before destroy

The beforeDestroy() method runs before the component has been removed to the DOM.

src/components/home.js
class Home extends Component {
beforeDestroy() {
// The component is not yet removed from the DOM
}

render() {
return <h2>Home</h2>;
}
}

After destroy

The afterDestroy() method runs after the component has been removed to the DOM.

src/components/home.js
class Home extends Component {
render() {
return <h2>Home</h2>;
}

afterDestroy() {
// The component is removed from the DOM
}
}

Props

Props are inputs accepted by components and injected from the route.

The function component accepts a single props object argument with data as a parameter. The class component has the same principle except that the props are exposed in the context of the class with this.props.

src/components/home.js
function Home(props) {
return `<h2>Hello, ${props.name}</h2>`;
}
http://localhost:3000

Hello, John Doe


Try it on CodeSandbox.

Nested components

Components can be nested and their props are automatically injected into the child component.

class Person extends Component {
render() {
return <h2>Hello, {this.props.name}</h2>;
}
}

class Home extends Component {
render() {
return <Person name="John Doe" />;
}
}
http://localhost:3000

Hello, John Doe

State changes

You should manually update the DOM on state changes. Making carefully crafted manual updates is much faster than using a virtual DOM with diff algorithms (and also less magic). Lifecycle hooks can be useful in making these updates at the right time.

The following examples show how to update the DOM when the state changes.

Updates the #counter element after each click.

src/components/counter.js
class Counter extends Component {
constructor(props) {
super(props);
this.counter = 0;
this.handleClick = this.handleClick.bind(this);
}

render() {
return (
<button onClick={this.handleClick}>
Clicks: <span id="counter">{this.counter}</span>
</button>
);
}

handleClick() {
this.counter++;
document.getElementById('counter').textContent = this.counter;
}
}

Try it on CodeSandbox.

Route data

The component class exposes the route data as a class property.

Signature

type route = {
path: string
params: {
[key: string]: string
};
};

Returns

Current path in URL and list of dynamic segments with their values.

Example

src/components/person.js
class Person extends Component {
render() {
return <h2>Person ID: {this.route.params.id}</h2>;
}
}

The above example with the route path /person/:id will expose the following data in the this.route object.

{
"path": "/person/42",
"params": {
"id": 42
}
}

Try it on CodeSandbox.