Quiero saber si es un antipatrón o si afecta al componente de alguna manera para hacer algo como esto:

render() {
  const MyFuncComponent = ({ prop1, prop2 }) => (
    // code here
  )

  return (
    <div>
      <MyFuncComponent prop1={something} prop2={else} />
    </div>
  )
}
3
corasan 17 sep. 2018 a las 19:21

3 respuestas

La mejor respuesta

Creo que, en general, las personas evitan definir funciones en render, pero de acuerdo con esta publicación de blog no es necesariamente una mala práctica. La publicación del blog se centra en las funciones del controlador de eventos en línea que se definen en render, pero supongo que se aplica a cualquier función definida en render. La definición de funciones en render significa que existe la sobrecarga de redefinirlas cada vez que se llama render pero eso puede no hacer una diferencia de rendimiento notable dependiendo de su componente.

Para el ejemplo particular que diste, recomendaría no definir otro componente de reacción en render. Si define alguna función en render, debe ser coherente con lo que está haciendo render. Definir otro componente o agregar un montón de funciones dentro del render puede hacer que sea difícil de manejar y difícil de entender lo que está haciendo el código.

2
ken_o 17 sep. 2018 a las 20:09

Un par de preguntas antes de hacer esto:

  1. ¿hace que el código sea más legible? (depende, digamos 50/50)
  2. ¿hace que el código sea más verificable? (no se puede burlar, acoplamiento apretado, así que "no")
  3. ¿hace que el código sea más flexible? (acoplamiento apretado - entonces "no" nuevamente)

Prefiero votar por hacer que el componente base obtenga uno interno a través de props. Y ese componente interno se crea en algún lugar afuera. Obtendría más flexibilidad, el código sería más legible debido a la abstracción, también con una mejor capacidad de prueba.

0
skyboyer 17 sep. 2018 a las 20:16

Sí, este es un antipatrón por la misma razón por la que no deberíamos usar un Componente de orden superior dentro de render.

No use HOCs en el interior el método de renderizado

El algoritmo diferente de React (llamado reconciliación) usa la identidad del componente para determinar si debe actualizar el subárbol existente o desecharlo y montar uno nuevo. Si el componente devuelto desde render es idéntico (===) al componente del render anterior, React actualiza recursivamente el subárbol al diferenciarlo con el nuevo. Si no son iguales, el subárbol anterior se desmonta por completo.

Normalmente, no debería necesitar pensar en esto. Pero es importante para los HOC porque significa que no puede aplicar un HOC a un componente dentro del método de representación de un componente:

render() {
  // A new version of EnhancedComponent is created on every render
  // EnhancedComponent1 !== EnhancedComponent2
  const EnhancedComponent = enhance(MyComponent);
  // That causes the entire subtree to unmount/remount each time!
  return <EnhancedComponent />;
}

El problema aquí no es solo el rendimiento: volver a montar un componente hace que se pierda el estado de ese componente y todos sus elementos secundarios.

Esto significa que el nuevo componente aparecerá en el árbol React (que se puede explorar con react-devtools ) pero no conservará ningún estado y los métodos de ciclo de vida como componentDidMount, componentWillUnmount, useEffect siempre llamarse cada ciclo de renderizado.

Soluciones

Dado que probablemente hay razones por las que queremos crear un componente de forma dinámica, aquí hay algunas soluciones comunes para evitar las trampas.

Defina el nuevo componente afuera

Ya sea en su propio archivo o directamente encima de la definición del componente principal.

const MyFuncComponent = ({ prop1, prop2 }) => <>{/* code here */}</>;

const MyComponent = props => (
  <div>
    {props.list.map(({ something, thing }) => (
      <MyFuncComponent prop1={something} prop2={thing} />
    ))}
  </div>
);

Función auxiliar

Una función regular que devuelve JSX se puede definir y usar directamente dentro de otro componente. No aparecerá como un nuevo componente dentro del árbol de React, solo aparecerá su resultado, como si estuviera en línea.

De esa manera, también podemos usar variables del ámbito de inclusión (como props.itemClass en el siguiente ejemplo) además de cualquier otro parámetro que desee.

const MyComponent = props => {
  // Looks like a component, but only serves as a function.
  const renderItem = ({ prop1, prop2 }) => (
    <li className={props.itemClass}> {/* <-- param from enclosing scope */}
      {prop1} {prop2} {/* other code */}
    </li>
  );

  return <ul>{props.list.map(renderItem)}</ul>;
};

También podría definirse fuera del componente, ya que es realmente flexible.

const renderItem = (itemClass, { prop1, prop2 }) => (
  <li className={itemClass}>
    {prop1} {prop2} {/* other code */}
  </li>
);

const MyComponent = props => (
  <ul>
    {props.list.map(item => renderItem(props.itemClass, item))}
  </ul>
);

Pero en ese punto, simplemente defina un componente Reaccionar en lugar de fingirlo con una función. Utilice React de manera predecible y en todo su potencial.

En línea la lógica

Es muy común incluir JSX en línea dentro de una condición o una devolución de llamada map.

const MyComponent = ({ itemClass }) => (
  <ul>
    {props.list.map(({ something, thing }) => (
      <li className={itemClass}>
        {something} {thing} {/* other code */}
      </li>
    ))}
  </ul>
);
2
Emile Bergeron 7 ene. 2020 a las 22:05