title: Are there any issues with defining a custom hook inside of a component?
body: <p>I have a component that uses the same React hook multiple times, using a prop passed to the component inside of the hook. For this example, I'm using the <code>useCallback</code> hook with the <code>finish</code> prop, which is just another function to be called:</p>
  
  <pre><code>const Example1 = ({ finish }) =&gt; {
    const runA = useCallback(
      () =&gt; {
        console.log("running A");
        finish();
      },
      [finish]
    );
    const runB = useCallback(
      () =&gt; {
        console.log("running B");
        finish();
      },
      [finish]
    );
  
    return (
      &lt;Fragment&gt;
        &lt;button onClick={runA}&gt;A&lt;/button&gt;
        &lt;button onClick={runB}&gt;B&lt;/button&gt;
      &lt;/Fragment&gt;
    );
  };
  </code></pre>
  
  <p>I'd like to DRY this up a bit by defining my own custom hook. My first attempt defined the hook directly inside of the component:</p>
  
  <pre><code>const Example2 = ({ finish }) =&gt; {
    const useCustomHook = action =&gt;
      useCallback(
        () =&gt; {
          action();
          finish();
        },
        [finish] // See note 1 below
      );
  
    const runA = useCustomHook(() =&gt; console.log("running A"));
    const runB = useCustomHook(() =&gt; console.log("running B"));
  
    return (
      &lt;Fragment&gt;
        &lt;button onClick={runA}&gt;A&lt;/button&gt;
        &lt;button onClick={runB}&gt;B&lt;/button&gt;
      &lt;/Fragment&gt;
    );
  };
  </code></pre>
  
  <blockquote>
    <p><strong>1</strong> — this dependency array should be <code>[action, finish]</code>. It's a mistake I made because I didn't have linting rules enabled when creating this example. I understand the importance of correctly specifying these.</p>
  </blockquote>
  
  <p><a href="https://codepen.io/shepmaster/pen/gVvGqj" rel="nofollow noreferrer">Full example on Codepen</a></p>
  
  <p>This appears to work, but the <a href="https://reactjs.org/docs/hooks-custom.html" rel="nofollow noreferrer">examples for creating custom hooks</a> all place the hook at the top-level of the file. The hooks FAQ <a href="https://reactjs.org/docs/hooks-faq.html#are-hooks-slow-because-of-creating-functions-in-render" rel="nofollow noreferrer">are hooks slow because of creating functions in render?</a> addresses the performance of creating functions, but the examples are functions passed <em>to</em> a hook.</p>
  
  <p>Are there any functional issues to be aware of with <em>creating</em> a hook inside of a component? If that's acceptable in general, are there any specific issues with capturing a property of the component in the hook?</p>
  
  <hr>
  
  <p>Some answers question why I don't want to place the hook outside the component. The primary reason is because I'm hoping to avoid the duplication and verbosity of such a solution, as shown by this quick demonstration:</p>
  
  <pre><code>const useTopLevelCustomHook = ({ finish, action }) =&gt;
    useCallback(
      () =&gt; {
        action();
        finish();
      },
      [action, finish]
    );
  
  const Example3 = ({ finish }) =&gt; {
    const runA = useTopLevelCustomHook({
      action: () =&gt; console.log("running A"),
      finish
    });
    const runB = useTopLevelCustomHook({
      action: () =&gt; console.log("running B"),
      finish
    });
  
    return (
      &lt;Fragment&gt;
        &lt;button onClick={runA}&gt;A&lt;/button&gt;
        &lt;button onClick={runB}&gt;B&lt;/button&gt;
      &lt;/Fragment&gt;
    );
  };
  </code></pre>
  
  <p>Some answers focus on the creation of the closures within the component, which was not my intent. In my original TypeScript app, I'm using Redux actions combined with a passed-in prop. The action creators are reasonably static, as they are imported:</p>
  
  <pre class="lang-js prettyprint-override"><code>import React, { useCallback } from 'react';
  import { useDispatch } from 'react-redux';
  
  import * as actions from './actions';
  
  interface BuildMenuProps {
    close: () =&gt; void;
  }
  
  const BuildMenu: React.SFC&lt;BuildMenuProps&gt; = props =&gt; {
    const dispatch = useDispatch();
  
    const useDispatchAndClose = (action: () =&gt; void) =&gt; useCallback(
      () =&gt; {
        dispatch(action());
        props.close();
      },
      [action, props, dispatch]
    );
  
    const compile = useDispatchAndClose(actions.performCompile);
    const compileToAssembly = useDispatchAndClose(actions.performCompileToAssembly);
    const compileToLLVM = useDispatchAndClose(actions.performCompileToLLVM);
    const compileToMir = useDispatchAndClose(actions.performCompileToMir);
    const compileToWasm = useDispatchAndClose(actions.performCompileToNightlyWasm);
    const execute = useDispatchAndClose(actions.performExecute);
    const test = useDispatchAndClose(actions.performTest);
  
    // JSX that uses these callbacks
  }
  </code></pre>
  
  <p>Contrast this to the hook outside the render function, which requires passing in <code>props.close</code>:</p>
  
  <pre class="lang-js prettyprint-override"><code>const useDispatchAndClose = (action: () =&gt; void, close: () =&gt; void) =&gt; {
    const dispatch = useDispatch();
  
    return useCallback(
      () =&gt; {
        dispatch(action());
        close();
      },
      [action, close, dispatch]
    );
  }
  
  const BuildMenu: React.SFC&lt;BuildMenuProps&gt; = props =&gt; {
    const compile = useDispatchAndClose(actions.performCompile, props.close);
    const compileToAssembly = useDispatchAndClose(actions.performCompileToAssembly, props.close);
    const compileToLLVM = useDispatchAndClose(actions.performCompileToLLVM, props.close);
    const compileToMir = useDispatchAndClose(actions.performCompileToMir, props.close);
    const compileToWasm = useDispatchAndClose(actions.performCompileToNightlyWasm, props.close);
    const execute = useDispatchAndClose(actions.performExecute, props.close);
    const test = useDispatchAndClose(actions.performTest, props.close);
  
    // JSX that uses these callbacks
  }
  </code></pre>