Source: input/InputOqt.js

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

/**
 * Componente que renderiza un input únicamente cuando tiene propiedades asignadas por parametros o variables (de lo contrario el componente será nativo)
 * @name Reactor.Components.Input.Input
 * @param {string} [options = {}] Json string con las propiedades React que se quieran asignar al componente
 * @param {("true"|"false")} [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 {("true"|"false")} [disabled = "false"] Si es true deshabilita el control
 * @param {("true"|"false")} [use_change = "true"] Si es true, al modificarse su valor desencadena el evento change del form que lo contiene
 * @class 
 * @example 
<input  
  name="{{InputName}}"
  use_local_storage="true"
  storage_name="_nombre"
  options='{"className":"input-class"}'
  value="Pepe"
/>
<input_oqt
  name="nombre"
  value="Pepe"
  change_var="variable"
  context="contexto"
/>
 */

export const InputOqt = React.memo( ({defaultValue, use_local_storage, storage_name, disabled = "false", options, children, ...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(props.autoFocus || (use_options && use_options.autoFocus)){
      inputRef && inputRef.current && inputRef.current.focus()
    }
  }, [])

  const newProps = {ref: inputRef, disabled: disabled === "true", value, onChange: handleChange, ...props, ...use_options};
  
  return <><input {...newProps} /> {children}</>
  
});