Javascript 在组件内部定义自定义挂钩是否存在任何问题?

Javascript 在组件内部定义自定义挂钩是否存在任何问题?,javascript,reactjs,closures,react-hooks,Javascript,Reactjs,Closures,React Hooks,我有一个组件多次使用同一个React钩子,使用一个传递到钩子内部组件的道具。在本例中,我将useCallback钩子与finish属性一起使用,这只是要调用的另一个函数: const Example1 = ({ finish }) => { const runA = useCallback( () => { console.log("running A"); finish(); }, [finish] ); const ru

我有一个组件多次使用同一个React钩子,使用一个传递到钩子内部组件的道具。在本例中,我将
useCallback
钩子与
finish
属性一起使用,这只是要调用的另一个函数:

const Example1 = ({ finish }) => {
  const runA = useCallback(
    () => {
      console.log("running A");
      finish();
    },
    [finish]
  );
  const runB = useCallback(
    () => {
      console.log("running B");
      finish();
    },
    [finish]
  );

  return (
    <Fragment>
      <button onClick={runA}>A</button>
      <button onClick={runB}>B</button>
    </Fragment>
  );
};
有些答案侧重于在组件内创建闭包,这不是我的意图。在我最初的TypeScript应用程序中,我使用了Redux操作和传入的道具。动作创建者在导入时相当静态:

import React,{useCallback}来自“React”;
从'react redux'导入{useDispatch};
将*作为操作从“/actions”导入;
接口构建程序{
关闭:()=>无效;
}
const BuildMenu:React.SFC=props=>{
const dispatch=usedpatch();
const useDispatchAndClose=(操作:()=>void)=>useCallback(
() => {
调度(action());
props.close();
},
[动作、道具、调度]
);
const compile=useDispatchAndClose(actions.performCompile);
const compileToAssembly=使用Dispatch和Close(actions.performCompileToAssembly);
const compileToLLVM=usedispatchendclose(actions.performcompiletolllvm);
const compileToMir=useDispatchAndClose(actions.performCompileToMir);
const compileToWasm=使用Dispatch和Close(actions.performCompileToNightlyWasm);
const execute=useDispatchAndClose(actions.performExecute);
常量测试=使用Dispatch和Close(actions.performTest);
//使用这些回调的JSX
}
这与渲染函数外部的钩子形成对比,后者需要传入
props.close

const useDispatchAndClose=(操作:()=>void,关闭:()=>void)=>{
const dispatch=usedpatch();
返回useCallback(
() => {
调度(action());
close();
},
[行动、关闭、调度]
);
}
const BuildMenu:React.SFC=props=>{
const compile=useDispatchAndClose(actions.performCompile,props.close);
const compileToAssembly=usedispatch和close(actions.performCompileToAssembly,props.close);
const compileToLLVM=usedispatchendclose(actions.performCompileToLLVM,props.close);
const compileToMir=useDispatchAndClose(actions.performCompileToMir,props.close);
const compileToWasm=使用Dispatch和close(actions.performCompileToNightlyWasm,props.close);
const execute=useDispatchAndClose(actions.performExecute,props.close);
常量测试=使用Dispatch和close(actions.performTest、props.close);
//使用这些回调的JSX
}
它是JavaScript,所以你可以做任何你想做的事情,但你应该问问自己这是否有意义

在组件中保留函数的唯一原因是它需要访问组件专用的变量。如果它是泛型(纯函数称之为),并将所有的PARAMs作为参数来工作,那么在哪里放置它并不重要,因为它与组件内部的放置相关联的并发症-为什么您仍然会考虑这样做呢?我建议把它放在组件外面。这很有道理

通过以下操作,使您更清楚地理解推理:

   const useCustomHook = action =>
    useCallback(
      () => {
        action();
        finish();
      },
      [finish]
    );
…您仍在为每次更新创建
useCustomHook
。对于
useCallback
,它应该是要创建的函数的包装器,如下所示:

    const useCustomHook = useCallback(action =>
      () => {
        action();
        finish();
      },
      [finish]
    );
每次更新都会创建传递给
useCustomHook
的箭头函数;这不是有效地否定了所有你不得不添加的恶作剧吗


只需将这些函数放在外部,您甚至不需要将其转换为自定义挂钩。

我不会说它是自定义挂钩,您只需将挂钩包装到回调。虽然它看起来像一个钩子,甚至被称为钩子,但它不是钩子,因为定制钩子的主要思想是从组件中提取可重复的逻辑以降低复杂性

我猜你对钩子想得太多了。 您的示例可以这样编写:

 const Example2 = ({ finish, depA, depB }) => {
  const executeAnAction = useCallback(
    action => {
      action();
      finish();
    },
    [finish]
  );

  const runA = useCallback(
    () => executeAnAction(() => console.log("running A", depA)),
    [depA, executeAnAction]
  );
  const runB = useCallback(
    () => executeAnAction(() => console.log("running B", depB)),
    [depB, executeAnAction]
  );

  return (
    <Fragment>
      <button onClick={runA}>A</button>
      <button onClick={runB}>B</button>
    </Fragment>
  );
};

const Example2=({finish,depA,depB})=>{
const executeAnAction=useCallback(
动作=>{
动作();
完成();
},
[完成]
);
const runA=useCallback(
()=>executeAnAction(()=>console.log(“运行一个”,depA)),
[depA,执行人员行动]
);
const runB=useCallback(
()=>executeAnAction(()=>console.log(“running B”,depB)),
[部门,执行人员行动]
);
返回(
A.
B
);
};
更简单一点

第二个原因:在您的示例中,自定义钩子使钩子的依赖关系保持在实际状态变得更加困难。事实上,
useCustomHook
应该有
[finish,action]
依赖项(因为action可以有自己的依赖项),像这样的插件会警告您。不幸的是,eslint插件react钩子将无法评估
useCustomHook
action
的依赖关系,但它在两个简单的
useCallback
的情况下可以正常工作,就像我的示例中一样


这里是。

正如其他答案中所述,这个问题实际上不是关于挂钩的,而是在功能组件内部创建函数是否可以。答案是肯定的,在功能组件中创建函数是完全可以接受的模式。您只需要知道,如果性能问题出现,您可能需要在事后进行一些优化。在
useCallback
中包装相当静态的函数已经在进行一些优化,所以我不认为这段代码会成为一个问题。

我认为您的方法没有问题。对于每个渲染,仍将以相同的顺序无条件调用所有挂钩。React在运行时无法区分
Example1
中的代码结构与React API的交互方式与
Example2
之间的区别

这似乎有效,但示例
    const useCustomHook = useCallback(action =>
      () => {
        action();
        finish();
      },
      [finish]
    );
 const Example2 = ({ finish, depA, depB }) => {
  const executeAnAction = useCallback(
    action => {
      action();
      finish();
    },
    [finish]
  );

  const runA = useCallback(
    () => executeAnAction(() => console.log("running A", depA)),
    [depA, executeAnAction]
  );
  const runB = useCallback(
    () => executeAnAction(() => console.log("running B", depB)),
    [depB, executeAnAction]
  );

  return (
    <Fragment>
      <button onClick={runA}>A</button>
      <button onClick={runB}>B</button>
    </Fragment>
  );
};