Source: input/TextAreaExt.js

import React, { useContext, useEffect, useRef, useState } from "react";
import { useDispatch } from "react-redux";
import { processOptions, FormContext, appActions, booleanString, localStorageGetItem, localStorageSetItem } from "../..";

/**
 * Componente que renderiza un textarea con opciones extendidas
 * @name Reactor.Components.Input.TextAreaExt
 * @param {JsonString} [options = {}] Json string con las propiedades React que se quieran asignar al componente
 * @param {BooleanString} [use_local_storage = "false"] Si es true el dato se guarda en el storage del explorador y la próxima vez que se renderice recordará el valor
 * @param {string?} storage_name El nombre que usara para guardar el dato en el storage, si no se pasa este parámetro se usara el name del input
 * @param {string?} change_var Variable que tomará el valor del control cuando se modifique
 * @param {string?} context Contexto donde se encuentra la variable
 * @param {BooleanString} [disabled = "false"] Si es true deshabilita el control
 * @param {BooleanString} [use_change = "true"] Si es true, al modificarse su valor desencadena el evento change del form que lo contiene
 * @param {BooleanString} [auto_focus = "false"] Determina si el control va a tomar el foco automaticamente
 * @class 
 * @example 
<textarea_ext  
  name="{{InputName}}"
  use_local_storage="true"
  storage_name="_nombre"
  options='{"className":"input-class"}'
  value="Pepe"
/>
<textarea_ext
  name="nombre"
  value="Pepe"
  change_var="variable"
  context="contexto"
/>
<textarea_ext
  name="nombreInput"
  var-value="nombreVariable"
  context="nombreContexto"
  auto_focus="true"
/>

 */

export const TextAreaExt = React.memo( ({defaultValue, use_local_storage, storage_name, disabled = "false", options, children, auto_focus = "false", ...props}) => {

  const inputRef = useRef();
  const use_storage =  use_local_storage === "true" && (storage_name || props.name);
  const var_storage = use_storage && `input_${storage_name || props.name}`;
  const use_options = processOptions(options); 
  const dispatch = useDispatch();

  const [value, setValue] = useState(() => use_storage ? localStorageGetItem(var_storage) : (defaultValue || props.value));
  const [loaded, setLoaded] = useState(false);
  const [selectionStart, setSelectionStart] = useState(0);

  const formContext = useContext(FormContext)

  const handleChange = (event) => {
    setSelectionStart(event.target.selectionStart);
    if (props.change_var && props.context) {
      dispatch(appActions.setVariable(props.change_var, event.target.value, props.context));
    }else{
      setValue(event.target.value);
    }
    if(use_storage){
      localStorageSetItem(var_storage, event.target.value);
    }
  }

  useEffect(()=>{
    if (inputRef.current.getAttribute("use_change") === "false") return;
    if (props.type!=="submit" && loaded) {
      formContext.change({target: inputRef.current});
    }
  }, [value]);

  useEffect(()=>{
    if(!use_storage && (defaultValue || props.value)){
      setValue(defaultValue || props.value);
      if(props.change_var && props.context && inputRef.current.selectionStart){
        setTimeout(
          ()=>{
            inputRef.current.selectionStart = selectionStart;
            inputRef.current.selectionEnd = selectionStart;    
          }, 10
        )
      }
    }
    if(!loaded) setLoaded(true);
  }, [defaultValue, use_storage, props.value, loaded]);

  useEffect(()=>{
    if(booleanString(auto_focus)){
      inputRef && inputRef.current && inputRef.current.focus();
    }
  }, [auto_focus])

  return (
    <>
      <textarea ref={inputRef} disabled={booleanString(disabled)} value={value} onChange={handleChange} {...props} {...use_options} />
      {children}
    </>
  )
  
});