title: React functional components: edit variable defined in function in a subfunction
body: <p>Sorry for the bad title. I don't really know how to describe my issue.</p>
  <p>I have a react functional component. It's meant to be text that you can drag to edit the value. An example is <a href="http://worrydream.com/Tangle/" rel="nofollow noreferrer">here</a>.</p>
  <p>To do this, I have 3 nested functions as the event listener callbacks. <code>mouseUp</code>, <code>mouseDown</code>, and <code>mouseDrag</code>. I have a variable defined in the component called ghostEle that I use to set the drag image. When you drag something like an image, there's a ghost image. That's why I'm doing this. However, when I set <code>ghostEle</code> to an element in a subfunction, it doesn't save. It says it's set but it doesn't set when the other functions are called. I have no idea why.</p>
  <p>Sorry if this is a bit unclear. Please add a comment if you don't get it.</p>
  <pre><code>// Value.tsx
  
  import React, { BaseSyntheticEvent } from &quot;react&quot;;
  
  
  
  import { trigger } from &quot;./helpers&quot;;
  import { StyliseN, AddS } from &quot;./index&quot;;
  
  /**
   * Type for properties of `REValue`. 
   */
  
  interface ValueCustomText {(val: number, unit: string): string};
  interface ValueCustomUnit {(val: number, rawunit?: string): string};
  
  interface ValueProps extends React.HTMLAttributes&lt;HTMLSpanElement&gt; {
      /**
       * ID for variable. Use this to link your `REValue` to an output, like `REOutput`.
       */
      id: string;
      /**
       * Default value for variable. You can set this so that when you first get onto your page, it shows your default value at first. And then, the user can change it.
       */
      value: number;
      /**
       * The unit. If it is set, it will add an 's' to the end if it has multiple or zero. If it isn't set, it will just be a plain number.
       */
      unit?: string;
      /**
       * Minimum value for the value. The user cannot drag lower than this. If unset, it will default to 0. You can set this to negative infinity by inputting `props.minvalue={-Infinity}`
       */
      minvalue?: number;
      /**
       * Maximum value for the value. The user cannot drag higher than this. If unset, it will default to Infinity.
       */
      maxvalue?: number;
      scalingrate?: number;
      stylish?: boolean;
      // number where it rounds to. default 1
      round?: number;
      getoutputtext?: ValueCustomText;
      getactualunit?: ValueCustomUnit;
  };
  
  /**
   * Component for draggable variable
   * @param {string} id ID of item. Can reference in other components
   * @param {number} value Default value of dragger
   * @param {string} [unit] The unit of the number. Don't add an 'S' at the end. _Optional_
   * @param {number} [minvalue] The minimum value of the dragger. You cannot set it below this. Default: `{0}`
   * @param {number} [maxvalue] The maximum value of the dragger. You cannot set it above this. Default: `{Infinity}`
   * @param {number} [scalingrate] The rate at which the number scales. It is a scalar. If `scalingrate=0.1`, it will scale 10 times slower. If it's `10`, it will scale 10 times faster. `-1` means that scaling is reversed. Default: `{1}`
   * @param {boolean} [stylish] If numbers should have decimals or commas. Default: `{true}`
   * 
   * Example without output:
   * ```tsx
   * return (
   *    &lt;REApp&gt;
   *      I ate &lt;REValue id=&quot;cookies&quot; value={3} unit=&quot;cookie&quot; minvalue={1} props.maxvalue={15} /&gt; today.
   *    &lt;/REApp&gt;
   * )
   * ```
   * 
   * Example with output:
   * ```tsx
   * // add later
   * ```
   */
  
  const Value: React.FC&lt;ValueProps&gt; = (props: ValueProps) =&gt; {
      const [value, setValue] = React.useState(props.value);
      const [active, setActive] = React.useState(false);
  
      const newval = {
          minvalue: props.minvalue ? props.minvalue : 0,
          maxvalue: props.maxvalue ? props.maxvalue : Infinity,
          round: props.round ? props.round : 1,
          scalingrate: props.scalingrate ? props.scalingrate : 1,
          stylish: props.stylish ? props.stylish: true,
      }
      
  
      let befX = -1;
  
      let ghostEle: any = false;
  
      function handleLoad() {
          trigger(`${props.id}:change`, {val: value, id: props.id});
      }
  
      React.useEffect(() =&gt; {
          console.log(&quot;MOUNTED&quot;);
          window.addEventListener(&quot;load&quot;, handleLoad);
          return function cleanup() {
              window.removeEventListener(&quot;load&quot;, handleLoad);
          }
      });
  
      function actualunit() {
          // custom stuff
          if (props.getactualunit) {
              return props.getactualunit(value, props.unit);
          }
          return props.unit ? (AddS(props.unit, value)) : &quot;&quot;;
      }
      function mouseDown(event: any) {
          // remove ghost element
  
          ghostEle = document.createElement(&quot;img&quot;);
          ghostEle.src = &quot;https://upload.wikimedia.org/wikipedia/en/9/95/Test_image.jpg&quot;;
          ghostEle.className = &quot;jankySolutionAGHHHPLSHELP&quot;
          // still has error and it doesn't even compile properly/ is there a problem or smthing?
  
          ghostEle.style.opacity = &quot;0&quot;;
          
          document.body.appendChild(ghostEle);
  
          event.dataTransfer?.setDragImage(ghostEle, -99999, -99999);
          event.dataTransfer.effectAllowed = &quot;none&quot;;
          // make it active
          setActive(true);
          console.log(ghostEle);
  
      }
      function mouseUp(event: any) {
          // remove ghost element
          console.log(ghostEle);
          document.body.removeChild(ghostEle);
          // make it inactive
          setActive(false);
          // reset befX
          befX = -1;
  
      }
      function mouseDrag(event: any) {
          // find value to set to beforehand
          const val = value + ((event.pageX - befX) * newval.scalingrate);
  
          // just to be safe prevent escalation of event
          if (event.pageX == 0) {
              event.preventDefault();
          }
          else if (!(val &gt;= newval.minvalue)) {
  
              befX = event.pageX;
              event.preventDefault();
          }
          else if (!(val &lt;= newval.maxvalue)) {
              befX = event.pageX;
              event.preventDefault();
          }
          else if (befX == -1) {
              befX = event.pageX;
              event.preventDefault();
          } else {
              // if its valid, do this
  
              //continue setting position of ghost image
              
              event.dataTransfer?.setDragImage(ghostEle, -99999, -99999);
  
              // set state of variable and also call render
              setValue(val);
  
              // set befX 
              if (event.pageX !== 0) {
  
                  befX = event.pageX;
              }
  
              // trigger event listener
              trigger(`${props.id}:change`, {val: value, id: props.id});
          }
          
          
      }
      const propstoadd = (({ id, value, unit, minvalue, maxvalue, scalingrate, stylish, getoutputtext, getactualunit, round, ...o }) =&gt; o)(props);
  // jos
      return (&lt;span {...propstoadd} data-value={value} className={&quot;reactive-essays-css-value reactive-essays-value-slider &quot; + (props.className ? &quot; &quot; + props.className : &quot;&quot;)} 
          id={props.id} 
          draggable={true} 
          onDragStart={mouseDown} 
          onDragEnd={mouseUp}
          onDrag={mouseDrag}&gt;{props.getoutputtext 
              ? props.getoutputtext(Math.round(value / newval.round) * newval.round, actualunit()) : 
              (newval.stylish ? 
                  StyliseN(Math.round(value / newval.round) * newval.round) : 
                  Math.round(value / newval.round) * newval.round)} {actualunit()}
      &lt;/span&gt;);
  }
  
  export default Value;
  </code></pre>