Uno de los retos más difíciles para alguien nuevo en React es entender el paradigma de componentes y su ciclo de vida. En versiones obsoletas de React todos los componentes era una clase, lo que significaba que era necesario utilizar un constructor y mantener un estado por componente. Esto no es necesariamente algo malo pero idealmente vamos a querer manejar un estado y todo el ciclo de vida de un componente únicamente en los componentes principales, los que se encuentran en un nivel lo suficientemente alto en jerarquía de nuestras aplicaciones y únicamente utilizar componentes funcionales como hijos, los cuales heredan toda su información de su componente padre.

Para entenderlo mejor, un componente estándar tiene de forma muy simplificada los siguientes componentes:

import React, { Component } from 'react';

class MiComponente extends Component {
    constructor(props) {
        super(props);

        this.state = {
            miVariableUno: "",
            miVariableDos: ""
        };
    };

    miFuncionUno = (parametroUno, parametroDos) => {
        // Contenido de la función
    };

    render() {
        return (
            <div>
                // Contenido del render
            </div>
        )
    };
};

export default MiComponente;

Siguiendo este paradigma, un componente hijo de nuestro primer componente sería declarado de la misma manera (como una clase) y para utilizar información proveniente de su componente padre la accede de la siguiente manera:

import React, { Component } from 'react';

class MiComponenteHijo extends Component {
    constructor(props) {
        super(props);

        this.state = {
            // Estado propio del componente hijo
        };
    };

    // Contenido del componente hijo

    render() {
        return (
            <div>
                {this.props.variableUno}
                <Button onClick={this.props.funcionUno}>Click</Button>
            </div>
        );
    };
};

Y tanto variableUno como funcionUno tendrían que ser explícitamente enviadas como parámetros desde el componente padre al hijo de la siguiente manera:

// Componente padre

render() {
    <MiComponenteHijo
        funcionUno={this.miFuncionUno}
        variableUno={this.state.miVariableUno} />
};

Entran los componentes funcionales

Todo lo anterior es perfectamente válido, sin embargo los componentes funcionales tienen una enorme ventaja sobre los componentes estándar: no manejan un estado propio ni tienen funciones propias. Básicamente son una plantilla que se llena con la información que se recibe del componente que los utiliza, tanto variables como funciones que se invocan desde eventos dentro del mismo.

Su sintaxis es mucho más sucinta:

import React from 'react';

const MiComponenteHijo = (props) => {
    return(
        <div>
            {props.variableUno}
            <Button onClick={props.funcionUno}>Click</Button>
        </div>
    );
};

export default MiComponenteHijo;

Un componente funcional depende enteramente del padre que le ha instanciado. Sin embargo ahí radica su gran ventaja, puesto que no es necesario reconciliar estados entre dos componentes o amarrar funciones manualmente entre ellos, el componente funcional toma las funciones del padre y se podría decir que se convierte en una extensión del mismo hasta cierto punto.

Un error muy común es que en un componente estándar accedemos a los parámetros que envía el componente padre mediante this.props. En un componente funcional declaramos props tal y como vemos en la línea 3 del ejemplo anterior como un parámetro del componente y accedemos a todo el contenido sin utilizar this, invocamos directamente props.