leogcba
5/18/2018 - 11:06 PM

React Notes

JSX

  • Una expresión JSX genera un elemento (Ver siguiente sección)
  • Cuando se escriban componentes multilineas, encerrarlos entre paréntesis para evitar "automatic semicolon insertion".
  • Los atributos de los componentes pueden ser:
    • String: van encerrados entre comillas dobles
    • Expresiones JS: van encerrados entre llaves
  • Instalar sublime-babel-vscode en Visual Studio Code para tener un correcto resaltado de JSX

Elementos

  • Los elementos son objetos JSON simples que representan los objetos DOM.
  • No es el elemento DOM, sino que es una representación simplificada del mismo.
  • A modo general, solo tiene dos campos: type: (string | ReactClass) and props: Object y los demás elementos anidados (dentro de las props).
  • No tienen métodos.
  • Los elementos son inmutables. Una vez que se crean, no pueden modificarse.
  • Son como los frames de una película, representan cómo debe verse la aplicación en un momento determinado
  • Para actualizar la UI, es necesario crear un nuevo elemento.
  • React DOM se encarga de actualizar solo los elementos de DOM que se han actualizado.

Componentes

  • Los componentes son piezas reutilizables de la UI
  • Sus nombres siempre deben empezar con mayúscula, sino React considera que son elementos HTML
  • Un componente NUNCA debe modificar sus propiedades
  • Los componentes reciben props como inputs y devuelven un elemento como output.

Propiedades

  • Deben ser inmutables
  • Se envían a traves de JSX como si fueran atributos HTML
  • Se reciben en el componente a través del objeto props

Estado

  • Es similar a las propiedades, pero son internas al componente
  • Solo está disponible para los "Componentes de clases"

Componentes funcionales

  • Son componentes definidos mediante una función
  • Reciben un único argumento props
  • Devuelven un componente
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

Componentes de clases

  • Son componentes definidos como una clase ES6 que extiende la clase React.Component
class Welcome extends React.Component {
 render() {
   return <h1>Hello, {this.props.name}</h1>;
 }
}
  • Tienen mas funcionalidades como estado y hooks para manejar el ciclo de vida del componente.

Estado

  • Se puede inicializar en el constructor de la siguiente manera:
constructor(props) {
   super(props);
   this.state = {
     someInitialValue: true
   }
 }
  • También se puede inicializar como una class property:
      class App extends React.Component {
        state = {
          someInitialValue: true
        }
    
        render() {
          // whatever you like
        }
      }
    

* Usar `this.setState({state: value})` para modificar el estado. Este método permite notificar que el componente debe re-renderizarse
* React puede juntar varias operaciones de actualización del estado y realizarlas en conjunto para mejorar la performance. Por lo tanto, no se debería calcular el próximo estado a partir del estado actual. Para ello, utilizar:
```Javascript
this.setState((prevState, props) => ({
 counter: prevState.counter + props.increment
}));
  • Solo se actualizan las propiedades indicadas a través de setState(). Las demás propiedades del estado se mergean con el nuevo estado.

Constructor

  • Siempre debería llamar al constructor base
  • De ser necesario, inicializar el estado
constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

componentDidMount()

  • Hook que se invoca cuando el componente se renderiza por primera vez
  • Es a donde se debe, por ejemplo, inicializar un timer, realizar una llamada a una API, ejecutar un action dispatcher de Redux.

componentWillUnmount()

  • Hook que se invoca cuando el componente se remueve del DOM
  • Es un buen lugar por ejemplo, para eliminar un timer o liberar recursos

Flujo unidireccional

  • Los componentes pueden enviarles informacion a los componentes anidados a través de sus propiedades
  • A los componentes anidados no les importa si el dato proviene de una propiedad o de su estado

Eventos

  • Usan formato camelCase
  • Para prevenir el comportamiento por defecto, los handlers deben llamar explícitamente a preventDefault:
function handleClick(e) {
  e.preventDefault();
  console.log('The link was clicked.');
}
  • Para establecer un listener, indicar el mismo al momento del renderizado en lugar de usar addEventListener:
render() {
return (
  <button onClick={this.handleClick}>Click me</button>
);

Binding this

Por defecto, en JS los métodos declarados en una clase no vinculan la variable thisa las instancias de la clase. Por lo tanto, si dentro de un método llamamos a this, el resultado será undefined. Esto se puede resolver de 3 maneras:

  • Invocando a la función bind en cada método en el constructor:
class Button extends React.Component {
  constructor(props) {
    super(props);
    this.state = {...};
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {...}
}
class Button extends React.Component {
  handleClick = () => {
    console.log('this is:', this);
  }
}
  • Usar una arrow function en el callback. No se lo recomienda porque crea un handler cada vez que se renderiza el componente:
render() {
    return (
      <button onClick={(e) => this.handleClick(e)}>Click me</button>
    );
  }

Renderizado condicional

  • Se pueden utilizar if-else, el operador condicional ternario y el if en línea con el operador &&:
{bool_condition && <a>Link</a>}
  • Si se desea que el mismo componente decida en función de su estado si no debe renderizarse, devolver null como resultado de la función o del método render.

Listas

  • Las listas de elementos/componentes pueden realizarse usando la función map() de JS.
  • Cada elemento/componente dentro de la función map() debe declarar un atributo key, de tipo string:
const todoItems = todos.map((todo) =>
  <li key={todo.id.toString()}>
    {todo.text}
  </li>
);
  • Si no se declara una key para cada elemento, React asignará como key el índice de la función map() y generará un WARNING
  • No se recomienda usar el index como key por cuestiones de performance
  • El atributo key NO es accesible en los componentes a través de las props

Formularios

  • Los elementos HTML <input>, <textarea> y <select> son diferentes al resto porque mantienen un estado interno relacionado con las interacciones con el usuario.
  • Los componentes React manejan su estado a través de la propiedad interna state.
  • Ambos estados se combinan con una técnica llamada "Controlled components"

Componentes controlados

  • Consiste en forzar a que los elementos HTML tomen su estado a partir del estado del componente React
  • La mayoría de los elementos HTML toman su valor a partir del estado del componente contenedor. Así mismo, el contenedor define el handler que controla las interacciones del usuario:
class Input extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  render() {
    return (
      <input type="text" value={this.state.value} onChange={this.handleChange} />
    );
  }
}
  • Los <textarea> también tienen una propiedad value para definir su contenido, en lugar de definirlo entre la etiqueta de apertura y cierre.
  • Los <select> también tienen una propiedad value para definir el elemento seleccionado, en lugar de definir el atributo selecteden la <option> correspondiente.
  • Un <select> también puede recibir múltiples values:
<select multiple={true} value={['B', 'C']}>
  • Los <input type="file">no se pueden manejar como elementos controlados, puesto que la propiedad value es read-only.

handleChange() genérico

handleInputChange(event) {
  const target = event.target;
  const value = target.type === 'checkbox' ? target.checked : target.value;
  const name = target.name;

  this.setState({
    [name]: value
  });
}

Lifting up state

Cuando se necesita que más de un componente compartan un estado común, se debe elevar el manejo de ese estado al primer ancestro común que estos tengan.

Composición vs Herencia

  • No se recomienda utilizar jerarquias de componentes. En su lugar se recomienda utilizar composición de componentes.
  • Se puede utilizar la propiedad especial props.childen para graficar elementos entre las etiquetas de apertura y cierre de un componente:
function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
}

function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">Welcome</h1>
    </FancyBorder>
  );
}
  • Si se necesitan propiedades adicionales, también se pueden utilizar:
function SplitPane(props) {
  return (
    <div className="SplitPane">
      <div className="SplitPane-left">{props.left}</div>
      <div className="SplitPane-right">{props.right}</div>
    </div>
  );
}

function App() {
  return (
    <SplitPane
      left={<Contacts />}
      right={<Chat />} 
    />
  );
}
  • Si hay funcionalidad común entre componentes, que no esté relacionada con la UI, se recomienda utilizar un módulo de JS e importarlo cuando sea necesario.

Pensando en React

  1. Crear un mock de la aplicación/componente a implementar y descomponer el mock en una jerarquía de componentes
  2. Construir una versión estática en React.
  • Los datos se cargar a partir de una colección de datos mock
  • Los datos se pasan a través de propiedades. Los componentes no tienen estado ya que el estado tiene que ver con la interacción. En esta etapa, los componentes TIENEN que ser estáticos.
  • Para aplicaciones pequeñas se recomienda un enfoque top-down.
  • Para aplicaciones grandes se recomienda un enfoque bottom-up, e ir realizando tests a medida que se desarrollan los componentes menores.
  1. Identificar el conjunto mínimo de estados requeridos. Para ello, identificar todas las piezas de información que participan en la aplicación. Luego, sobre cada pieza de información, preguntarse:
  • Si la información es recibida desde el padre, probablemente no es estado
  • Si la información no cambia en el tiempo, probablemente no es estado
  • Si la información puede ser calculada (en base a otras props/state), NO ES UN ESTADO
  1. Identificar a quien pertence cada estado:
  • Identificar que componentes requieren cada pieza de información
  • Posiblemente, el estado podría vivir en el primer ancestro común a esos componentes
  • Si no es coherente que ningún componente contenga ese estado, crear un componente solo para alojar el estado e insertarlo en algún lugar de la jerarquía (siempre que sea común a quienes requieren de ese estado)
  1. Agregar event handlers. Como el elemento que contiene el estado es quien puede actualizarlo, allí se deben definir los event handlers. Luego, pasar esos event handlers como callbacks a los componentes hijos.