Posibilidades de manejo de focus en react
===
## 1. Refs (definición y consideraciones de uso)
Modifica un elemento hijo fuera del flujo de la data de forma imperativa. El elemento hijo a modificar puede ser una instancia de un componente o un elemento del DOM.
- Consideraciones
- No usar atributos Ref en componentes que sean una función, ya que no tiene instancias
- Excepciones: la única forma de utilizar refs es dentro de un componente funcional es referenciando a un elemento del DOM o a un componente de clase.
- No abusar del uso de ref
- Evitar su uso en instancias donde se pueda manejar algo de forma declarativa
## 1.1 Crear Refs (para versiones de react 16.3 o superiores)
En general, se crea en el constructor para poder ser referenciado con sus atributos a lo largo del componente.
#### Sintaxis
```
React.createRef()
```
#### Ejemplo
```
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.input = React.createRef();//creación del Ref
}
render() {
return <div ref={this.input} />
}
}
```
## 1.2 Accediendo a Refs (en render)
Cuando se pasa un Ref a un elemento del render, se puede referenciar al nodo accediendo al ACTUAL atributo del Ref
#### Sintaxis
```
const node = this.myRef.current;
```
#### Consideraciones
El valor del Ref depende del tipo de nodo:
- Cuando el atributo del ref es usado en un elemento HTML, el ref creado en el constructor recibe el elemento del DOM subyacente como su propiedad ACTUAL
-
- Cuando el atributo del ref es usado en un componente de clase, el ref recibe la instancia MONTADA del componente como su propiedad ACTUAL
### 1.2.1 Manejar focus con Ref a un elemento del DOM
React va a asignar la propiedad actual con el elemento del DOM cuando el componente esté montado y lo asignará null cuando se desmonte. La actualización del ref sucede ANTES de los ciclos de vida componentDidUpdate y componentDidMount
#### Ejemplo
```
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// crea un ref para guardar el elemento del DOM textInput
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
//Explicita el focus del input de texto usando DOM API
//Se accede al current para obtener el nodo del DOM
this.textInput.current.focus();
}
render() {
// Se le indica a React que queremos asociar el ref de
//<input> con el 'textInput' que creamos en el constructor
return (
<div>
//recibe el elemento del DOM como propiedad actual
<input
type="text"
ref={this.textInput} />
//llama al método de focus
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
```
### 1.2.2 Manejar focus con Ref a un componente de clase
Para envolver un componente de clase para simular que sea clickeado inmediatamente después de montarlo, podemos usar un ref para acceder a el elemento creado y llamar el método de focus manualmente.
#### Ejemplo
```
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef(); //Ref creado
}
componentDidMount() {
// se accede al nodo
//se llama el método de focus manualmente
this.textInput.current.focusTextInput();
}
render() {
return (
//recibe la instancia montada como actual
//CustomTextInput DEBE ser declarado como componente de clase
<CustomTextInput ref={this.textInput} />
);
}
}
```
### 1.2.3 Manejar focus con Ref en componentes funcionales
Para usar refs en este tipo de componentes, hay 2 opciones:
- Convertir el componente a uno de clase, como cuando es necesario añadir ciclos de vida o state a un componente
- Utilizar un atributo ref DENTRO del componente funcional, siempre y cuando se haga referencia a un elemento del DOM o a un componente de clase
#### Ejemplo de uso de atributo ref DENTRO de un componente funcional
```
function CustomTextInput(props) {
// textInput DEBE ser declarado aquí para poder referenciarlo
let textInput = React.createRef();
function handleClick() {
//Se accede al valor actual del nodo
//se llama el focus manualmente dentro del handler
textInput.current.focus();
}
return (
<div>
//Se referencia el elemento del DOM
<input
type="text"
ref={textInput} />
//Se llama al handler
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
```
## 1.3 Hacer focus a un elemento hijo de un componente con Ref
Cuando un componente padre necesita poner un focus en un elemento de un componente hijo podemos exponer el ref del elemento DOM del padre a través de un prop especial en el componente hijo, que reenvía el ref del padre al nodo del DOM del elemento hijo.
```
//Componente hijo
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} /> //acá se asignan los props
</div>
);
}
//Componente padre
class Parent extends React.Component {
constructor(props) {
super(props);
this.inputElement = React.createRef();//Ref creado
}
render() {
return (
//referencia al elemento DOM del componente hijo
<CustomTextInput inputRef={this.inputElement} />
);
}
}
// Accediendo al nodo del elemento DOM del hijo
//ahora se puede establecer el focus donde sea requerido
this.inputElement.current.focus();
```
## 1.4 Callback Refs para manejo de focus
En vez de pasar un atributo ref creado con `createRef()`, se pasa una función. La función recibe la instancia del componente o el elemento del DOM como argumento. Puede ser guardado y llamado desde cualquier parte.
- React va a llamar al callback ref con el elemento DOM cuando se monte el componente y será null cuando se desmonte.
- Se actualiza el ref antes de que se ejecuten los ciclos de vida `componentDidMount` o `componentDidUpdate`
#### Ejemplo
```
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = element => {
this.textInput = element;
};
this.focusTextInput = () => {
// Focus en el texto usando DOM API
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// autofocus en el input montado
this.focusTextInput();
}
render() {
//Usa el `ref` callback para guardar la referencia del elemento del DOM
return (
<div>
<input
type="text"
ref={this.setTextInputRef}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
```
- Se pueden pasar callbacks entre componentes, como con refs que fueron creados con `React.createRef()`
```
//Componente hijo
function CustomTextInput(props) {
return (
<div>
//props que recibe del componente padre
<input ref={props.inputRef} />
</div>
);
}
//Componente padre
class Parent extends React.Component {
render() {
return (
//Accede al elemento DOM del componente hijo
<CustomTextInput
inputRef={el => this.inputElement = el} //callback
/>
);
}
}
```
## 2. Guía rápida para manejar focus con refs
### 2.1 Ref a un elemento del DOM
- Componente
- Constructor
- [ ]`this.example = React.createRef();`
- [ ]`this.focusExample = this.focusExample.bind(this)`
- Funciones
- [ ] `focusExample(){this.example.current.focus()}`
- Render
- [ ]`<input type="text"
ref={this.example}/>`
- [ ]`<input
type="button"
value="any"
onClick={this.focusExample} />`
### 2.2 Ref a un componente de clase
- Componente
- Constructor
- [ ]`this.example = React.createRef();`
- Ciclo de vida
- [ ] `componentDidMount() {
this.example.current.focusExample();
}`
- Render
- [ ] `<ClassComponentImported ref={this.example} />`
### 2.3 Ref en componentes funcionales
- Opción 1:
- [ ] Convertir el componente a uno de clase
- Opción 2:
- Constructor
- [ ] `let example = React.createRef();`
- Handlers
- [ ] `function handleClick() {example.current.focus();}`
- Render
- [ ] `<input
type="text"
ref={example} />`
- [ ] `<input
type="button"
value="any"
onClick={handleClick}
/>`
### 2.4 Focus a un elemento hijo de un componente con Ref
- Componente Hijo
- Render
- [ ] `<input ref={props.inputRef} />`
- Componente Padre
- Constructor
- [ ] `this.inputElement = React.createRef()`
- [ ] `this.inputElement.current.focus();`
- Render
- [ ] `<CustomTextInput inputRef={this.inputElement} />`
### 2.5 Callback
- Componente
- Constructor
- [ ] `this.textInput = null;`
- [ ] ` this.setTextInputRef = element => {
this.textInput = element;
};`
- [ ] ` this.focusTextInput = () => {
// Focus en el texto usando DOM API
if (this.textInput) this.textInput.focus();
};`
- Ciclos de vida
- [ ] ` componentDidMount() {
// autofocus en el input montado
this.focusTextInput();
}`
- Render
- [ ] ` <input
type="text"
ref={this.setTextInputRef}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>`