Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/466.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何从工厂函数返回React Memorized回调_Javascript_Reactjs_React Hooks - Fatal编程技术网

Javascript 如何从工厂函数返回React Memorized回调

Javascript 如何从工厂函数返回React Memorized回调,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,我想知道在使用工厂生成回调函数时,如何最好地在React中返回一个已记忆的回调函数。目标是在使用相同参数调用工厂时返回相同的函数实例 例如: function MyComponent() { // This will always return a new function const makeCallback = param => () => { /* perform action with 'param' */ }; return

我想知道在使用工厂生成回调函数时,如何最好地在React中返回一个已记忆的回调函数。目标是在使用相同参数调用工厂时返回相同的函数实例

例如:

function MyComponent() {
    // This will always return a new function
    const makeCallback = param => () => {
        /* perform action with 'param' */
    };

    return (
        <>
            <Button onClick={makeCallback('foo')} />
            <Button onClick={makeCallback('bar')} />
            <Button onClick={makeCallback('baz')} />
        </>
    );
但是,这是不允许的,并且在构建时失败了

React Hook“React.useCallback”在函数“makeCallback”中调用,该函数既不是React函数组件,也不是自定义React Hook函数-React hooks/rules of hooks

他们明确表示不允许在嵌套函数中调用钩子,但这对我来说似乎很奇怪,因为自定义钩子通常只是调用其他钩子的函数。它说首要的问题是保持执行的顺序,但我不认为这在本案中会被违反

我最好的选择是将我的工厂变成一个钩子,并在每个案例的顶层显式调用它吗?我更喜欢在按钮本身中构建回调的简单性,因为它的输入量少了一点,而且当与按钮一起使用时,param片段更明显

// Same factory function, but now it's magically a "hook"
const useCallbackFactory = param => {
    return React.useCallback(() => { /* do 'param' stuff */ }, [param]);
}

function MyComponent() {
    // Define the callbacks ahead of time
    const fooCb = useCallbackFactory('foo');
    const barCb = useCallbackFactory('bar');
    const bazCb = useCallbackFactory('baz');

    return (
        <>
            <Button onClick={fooCb} />
            <Button onClick={barCb} />
            <Button onClick={bazCb} />
        </>
    );
}
//相同的工厂函数,但现在它神奇地变成了一个“钩子”
const useCallbackFactory=param=>{
返回React.useCallback(()=>{/*do'param'stuff*/},[param]);
}
函数MyComponent(){
//提前定义回调
const fooCb=useCallbackFactory('foo');
const barCb=useCallbackFactory('bar');
const bazCb=useCallbackFactory('baz');
返回(
);
}

一种可能的方法是创建自定义挂钩: (不要把这个例子看得太严肃,只是为了说明问题)

const useFactory=(类型)=>{
让f;
如果(类型=='a'){
f=()=>console.log('helloworld');
}否则{
f=()=>console.log('byeworld');
}
return React.useCallback(f[type]);
};
const Button=React.memo({onClick,children})=>{
console.log(“重新呈现”);
返回{children};
});
常量应用=()=>{
常数f1=使用工厂('a');
常数f2=使用工厂('b');
返回(
一层楼
一层楼
);
};

目标是在工厂运行时返回相同的函数实例 使用相同的参数调用

我想你想要的是这样的:

 let memoizedcb = React.useCallback(
    memoize((fieldName) => (val) => doSomething(fieldName, val)),
    []
  );
在哪里

import memoize from "fast-memoize";
现在,对于同一个参数,
memoizedcb
返回的函数在不同的渲染中是相同的

现在你可以像这样使用它了

<TextField onChange={memoizedcb("name")} value={name}/>
<TextField onChange={memoizedcb("surname")} value={surname}}/>

我认为您已接近
useCallback
,但您真的只想记住工厂的结果。
usemo
hook可以做到这一点

const callback = useMemo(() => {
  return callbackFactory(param);
}, [param]);
除非依赖项数组中的
param
发生更改,否则钩子不会重新计算其返回值(即回调)

演示

const callbackFactory=param=>{
log(“调用的回调工厂!!”;
开关(参数){
案例“a”:
return()=>console.log(“回调A”);
案例“b”:
return()=>console.log(“回调B”);
违约:
return()=>{};
}
};
导出默认函数App(){
const[param,setParam]=使用状态(“a”);
const[,setCount]=使用状态(0);
const callback=useMoom(()=>{
返回callbackFactory(参数);
},[param]);
返回(
你好,代码沙盒
开始编辑,看看神奇的发生!
setParam(e.target.value)}>
{[“a”,“b”].map(参数=>(
{param}
))}
按我
集合计数(c=>c+1)}>
重播!
);
}

如果组件的不同“实例”和组件的重新安装之间的回调应该保持不变,那么在组件范围之外提前声明它们可能是有意义的

const cb1 = params => { ... };
const cb2 = params => { ... };

const App = () => (
  <div>
    <Button onClick={cb1}>F1</Button>
    <Button onClick={cb2}>F2</Button>
  </div>  
);

这是非常不幸的,但是react不支持这样的东西。我发现最好的方法是重用相同的记忆回调,并使用
name
作为id,如下所示(修改代码片段):

函数MyComponent(){
常量回调=使用回调(e=>{
const param=e.target.name
/*使用“param”执行操作*/
}, []);
返回(
);
}
同样,在复杂的场景中,您可以在子对象中传递某种id,子对象可以在回调中将其传递回:

函数父函数(){
const callback=useCallback((e,param)=>{
/*使用“param”执行操作*/
}, []);
返回
}
const Child=memo({id,onChange})=>{
返回onChange(e,id)}/>
})

尽管从表面上看,您似乎在子对象中创建了一个新函数,(已记忆的)子对象本身不会在每个父对象渲染时重新渲染。

这看起来与我问题中的最后一个片段非常相似。我相信这对我来说是可行的,但是如果我可以的话,我想避免这个额外的样板。我没有考虑使用一个单独的记忆库来处理工厂的内部设备——如果钩子会引起麻烦,也许这就是妥协。我对备忘录的内部结构不是很熟悉,所以你知道除了React之外,使用不同的库会有什么好处/损失吗?你会推荐这种“快速记忆”而不是像“lodash”这样的东西吗?@J.Sheets我不认为会有任何损失,快速记忆在英国非常流行。我在我的项目中使用了这种方法。不幸的是,我认为这仍然需要为工厂预先定义参数,这是通过示例中的“select”元素完成的。不过,我喜欢这个示例,对于通过UI.@J.Sheets“构造”回调的情况,添加select只是为了在工厂的参数更新时显示/模拟备忘录的工作情况。您可以看到单击“重新加载”按钮时,组件将重新加载到工厂中
const callback = useMemo(() => {
  return callbackFactory(param);
}, [param]);
const callbackFactory = param => {
  console.log("invoked callback factory!!");
  switch (param) {
    case "a":
      return () => console.log("callback A");

    case "b":
      return () => console.log("callback B");

    default:
      return () => {};
  }
};

export default function App() {
  const [param, setParam] = useState("a");
  const [, setCount] = useState(0);

const callback = useMemo(() => {
  return callbackFactory(param);
}, [param]);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>

      <select defaultValue="a" onChange={e => setParam(e.target.value)}>
        {["a", "b"].map(param => (
          <option key={param} value={param}>
            {param}
          </option>
        ))}
      </select>

      <button type="button" onClick={callback}>
        Press Me
      </button>
      <button type="button" onClick={() => setCount(c => c + 1)}>
        Rerender!
      </button>
    </div>
  );
}
const cb1 = params => { ... };
const cb2 = params => { ... };

const App = () => (
  <div>
    <Button onClick={cb1}>F1</Button>
    <Button onClick={cb2}>F2</Button>
  </div>  
);