React-defaultProps vs ES6 parámetros predeterminados al desestructurar (problemas de rendimiento)


Acabo de encontrar una pregunta sobre el rendimiento de React cuando se configuran los valores predeterminados en uno de mis componentes funcionales sin estado.

Este componente tenía un defaultProps que definía row: false, pero no me gustó porque el defaultProps es al final del archivo, lo que en realidad hace que sea más difícil de ver. Y por lo tanto, no somos conscientes de la propiedad predeterminada. Así que lo moví a la declaración de función directamente y lo asigné usando el valor predeterminado de ES6 para parámetros.

const FormField = ({
  row = false,
  ...others,
}) => {
  // logic...
};

Pero luego discutimos con un compañero de trabajo acerca de que esto era una buena idea o no. Porque hacerlo puede parecer trivial, pero también puede tener un gran impacto en las actuaciones ya que react no es consciente del valor predeterminado.

Creo que en este caso, es trivial. Porque es un booleano y no un objeto/array y por lo tanto no será visto como un valor diferente durante reconciliación.


Pero, vamos a ver ahora un más avanzado caso de uso:

const FormField = ({
  input: { name, value, ...inputRest },
  label = capitalize(name),
  placeholder = label,
  row = false,
  meta: { touched, error, warning },
  ...others,
}) => {
  // logic...
};

Aquí, baso el valor de placeholder de label, que a su vez se basa en input.name. El uso de ES6 desestructuring con valores predeterminados para los parámetros hace que todo sea bastante fácil de escribir / entender y funciona como un encanto.

Pero es una buena idea? Y si no, entonces, ¿cómo lo harías correctamente?

Author: Vadorequest, 2017-01-05

2 answers

Hablé con varias personas en el canal Discord #reactiflux y realmente obtuve la respuesta que estaba buscando.

Hay básicamente tres casos de uso con los componentes de React, y en algunos de ellos, los parámetros de desestructuración impactarán en las actuaciones, por lo que es importante entender lo que está pasando hunder the hood.

Componente funcional sin estado

const MyComponent = ({ name = 'John Doe', displayName = humanize(name), address = helper.getDefaultAddress() }) => {
  return (
    <div>{displayName}</div>
  );
};

Este es un componente funcional sin estado. No hay estado, y es funcional porque no es un Class instancia, pero una función simple.

En este caso, no hay ciclo de vida, no se puede tener un componentWillMount o shouldComponentUpdate o constructor allí. Y debido a que no hay gestión del ciclo de vida, no hay impacto en el rendimiento en absoluto. Este código es perfectamente válido. Algunos pueden preferir manejar el valor predeterminado displayName dentro del cuerpo de la función, pero al final realmente no importa, no afectará las actuaciones.

Componente no funcional apátrida

(No hacer ¡esto!)

class MyComponent extends React.Component {
    render() {
        const { name = 'John Doe', displayName = humanize(name), address = helper.getDefaultAddress() } = this.props;
        return (
            <div>{displayName}</div>
          );
    }
}

Este es un componente no funcional sin estado. No hay estado, pero no es "funcional" ya que es un class. Y porque es una clase, extendiendo React.Component, significa que usted tendrá un ciclo de vida. Puedes tener componentWillMount o shouldComponentUpdate o constructor allí.

Y, debido a que tiene un ciclo de vida, la forma de escribir este componente es malo. ¿Pero por qué?

En pocas palabras, React ofrece un atributo defaultProps, para tratar con los valores de props predeterminados. Y en realidad es es mejor usarlo cuando se trata de componentes no funcionales, porque será llamado por todos los métodos que dependen de this.props.

El fragmento de código anterior crea nuevas variables locales llamadas name y displayName, pero los valores predeterminados se aplican solo para este método render!. Si desea que se apliquen los valores predeterminados para cada método, como los del ciclo de vida de React (shouldComponentUpdate, etc.) entonces debe usar el defaultProps en su lugar.

Entonces, el código anterior es en realidad un error que puede llevar a malentendidos sobre el valor predeterminado de name.

Aquí es cómo se debe escribir en su lugar, para obtener el mismo comportamiento:

class MyComponent extends React.Component {
    render() {
        const { name, displayName = humanize(name), address } = this.props;
        return (
            <div>{displayName}</div>
          );
    }
}

MyComponent.defaultProps = {
    name: 'John Doe',
    address: helper.getDefaultAddress(),
};

Esto es mejor. Porque el nombre siempre será John Doe si no fue definido. address también se trató el valor predeterminado, pero no displayName... ¿Por qué?

Bueno, todavía no he encontrado una manera de evitar ese caso de uso especial. Porque el displayName debe basarse en la propiedad name, a la que no podemos acceder (AFAIK) cuando definiendo defaultProps. La única manera que veo es tratar con él en el método render directamente. Tal vez haya una mejor manera.

No tenemos este problema con la propiedad address ya que no se basa en las propiedades MyComponent, sino que depende de algo totalmente independiente que no necesita los apoyos.

Componente no funcional con estado

Funciona exactamente igual que "Componente no funcional sin estado". Debido a que todavía hay un ciclo de vida, el comportamiento será el mismo. El el hecho de que haya un state interno adicional en el componente no cambiará nada.


Espero que esto ayude a entender cuando se usa desestructuración con componentes. Me gusta mucho la forma funcional, es mucho más limpio en mi humilde opinión (+1 para la simplicidad).

Es posible que prefiera usar siempre defaultProps, ya sea que trabaje con componentes funcionales o no funcionales, también es válido. (+1 para coherencia)

Solo tenga en cuenta el ciclo de vida con componentes no funcionales que "requiere" el uso de defaultProps. Pero al final la elección es siempre tuya;)

 17
Author: Vadorequest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-11 09:17:51

Mirando el caso de uso avanzado que tiene, está agregando propiedades innecesarias al componente. label y placeholder dependen de que se pasen otras propiedades y, en mi opinión, no deben enumerarse como un parámetro del propio componente.

Si estuviera tratando de usar <FormField /> en mi aplicación y necesitara mirar para ver qué dependencias tiene ese componente específico, estaría un poco confundido en cuanto a por qué está creando parámetros que se basan en otros parámetros. Me movería el label y placeholder en el cuerpo de la función por lo que es claro no son dependencias de componentes, sino simplemente efectos secundarios.

En lo que respecta al rendimiento aquí, no estoy seguro de que haya una diferencia significativa de cualquier manera. Los componentes sin estado realmente no tienen una 'instancia de respaldo' que los componentes con estado, lo que significa que no hay un objeto en memoria que realice un seguimiento del componente. Creo que es sólo una función pura de pasar parámetros en y devolviendo la vista.

En esa misma nota.. agregar los PropTypes ayudará con la comprobación de tipos.

const FormField = ({
  input: { name, value, ...inputRest },
  row = false,
  meta: { touched, error, warning },
  ...others,
}) => {
  const label = capitalize(name),
  const placeholder = label,

  return (
    // logic
  );
};

FormField.propTypes = {
  input: PropTypes.shape({
    name: PropTypes.string.isRequired,
    value: PropTypes.string,
  }).isRequired,
  meta: PropTypes.shape({
    touched: PropTypes.bool.isRequired,
    error: PropTypes.bool.isRequired,
    warning: PropTypes.bool.isRequired,
  }).isRequired,
  row: PropTypes.bool.isRequired,
};
 1
Author: Taylor Jones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-02 16:26:37