Reactjs React自定义挂钩和useMemo挂钩

Reactjs React自定义挂钩和useMemo挂钩,reactjs,react-hooks,react-usememo,Reactjs,React Hooks,React Usememo,我有两个“昂贵”的函数,我想在我的react应用程序中记住它们,因为它们需要很长时间才能呈现。昂贵的函数用于数组映射。我希望记住数组映射的每个结果,这样,如果数组中的一个元素发生了更改,则只会重新计算该元素昂贵的函数。(并且独立地记忆昂贵的函数,因为有时只需要重新计算一个函数。)我正在努力记忆和传递当前数组值 以下是正在运行的演示,没有备忘录: import React, { useMemo, useState } from "react"; const input = [

我有两个“昂贵”的函数,我想在我的react应用程序中记住它们,因为它们需要很长时间才能呈现。昂贵的函数用于数组映射。我希望记住数组映射的每个结果,这样,如果数组中的一个元素发生了更改,则只会重新计算该元素昂贵的函数。(并且独立地记忆昂贵的函数,因为有时只需要重新计算一个函数。)我正在努力记忆和传递当前数组值

以下是正在运行的演示,没有备忘录:

import React, { useMemo, useState } from "react";

const input = ["apple", "banana", "cherry", "durian", "elderberry", "apple"];

export default function App() {
  const output = input.map((msg, key) => createItem(msg, key));
  return <div className="App">{output}</div>;
}

const createItem = (message, key) => {   // expensive function 1
  console.log("in createItem:", key, message);

  return (
    <div key={key}>
      <strong>{"Message " + (1 + key)}: </strong>
      {message} =>{" "}
      <span
        id={"preview-" + key}
        dangerouslySetInnerHTML={{ __html: msgSub(message) }}
      />
    </div>
  );
};

const msgSub = message => {   // expensive function 2
  const messageSub = message.replace(/[a]/g, "A").replace(/[e]/g, "E");
  console.log("in msgSub:", message, messageSub);
  return messageSub;
};
import React,{usemo,useState}来自“React”;
常量输入=[“苹果”、“香蕉”、“樱桃”、“榴莲”、“接骨木”、“苹果”];
导出默认函数App(){
const output=input.map((消息,键)=>createItem(消息,键));
返回{output};
}
const createItem=(消息,键)=>{///函数1
log(“在createItem:”,键,消息中);
返回(
{“消息”+(1+键)}:
{message}=>{”“}
);
};
const msgSub=message=>{///函数2
const messageSub=message.replace(/[a]/g,“a”).replace(/[e]/g,“e”);
log(“在msgSub:,message,messageSub中”);
返回messageSub;
};
(我没有让它在SO的编辑器上运行,所以请查看并运行它。)

这里是使用自定义钩子和useMemo钩子

任何指导都将不胜感激


以及演示如何在SO的编辑器中对工作做出反应的额外点数

使项目成为纯组件:

const id=((id)=>()=>id++)(1)//iLife创建id
//纯组分
const Item=React.memo(函数项({increase,Item}){
log('rendering id:',item.id);
返回(
    增加(item.id)}> {item.count}
); }); 常量应用=()=>{ const[items,setItems]=React.useState([]); //使用回调,仅在安装应用程序时增加 常量增加=React.useCallback( (id)=> 设置项目((项目)=> //使用状态设置器回调 //useCallback没有依赖项,也没有过时 //闭包 items.map((item)=> item.id==id ?{…项,计数:项.计数+1} :项目 ) ), []//没有依赖项 ); 返回( 设置项目((项目)=>[ {id:id(),计数:0}, …项目, ]) } > 添加计数器
    {items.map((item)=>( ))}
); }; ReactDOM.render(,document.getElementById('root'))
我的第一步是更改
createItem
,使该项成为其唯一的功能组件。这意味着我可以记忆
组件,这样它只有在道具更改时才会呈现,重要的是消息和键/索引(正如您之前呈现的值)

这方面的工作示例

const Item=React.memo({message,index})=>{
日志(“呈现:”,索引,消息);
const calculatedMessage=msgSub(消息);
返回(
{“消息”+(1+索引)}:
{message}=>{”“}
);
});
const msgSub=消息=>{
//昂贵的功能2
const messageSub=message.replace(/[a]/g,“a”).replace(/[e]/g,“e”);
log(“在msgSub:,message,messageSub中”);
返回messageSub;
};
您可以看到,在初始渲染时,它会渲染所有项目及其
消息
,尤其是
apple
两次

如果两个组件彼此独立渲染,并且恰好使用相同的道具,则将渲染这两个组件。React.memo不保存组件渲染

它必须呈现项目组件两次
,一次用于索引0处的
apple
,另一次用于索引5处的apple


您会注意到我在应用程序中放置了一个按钮,单击该按钮将更改数组的内容

稍微更改一下原始数组,我将
carrot
放在原始数组的索引4处,并在更新时将其移动到新数组的索引2处

  const [stringArray, setStringArray] = React.useState([
    "apple",
    "banana",
    "cherry",
    "durian",
    "carrot",
    "apple" // duplicate Apple
  ]);

  const changeArray = () =>
    setStringArray([
      "apple",
      "banana",
      "carrot",  // durian removed, carrot moved from index 4 to index 2
      "dates", // new item
      "elderberry", // new item
      "apple"
    ]);
如果您查看控制台,您将看到在第一次渲染时在msgSub:carrot carrot中看到
,但是当我们更新数组时,会再次调用msgSub:carrot carrot
中的
。这是因为
上的键被强制重新启动。这是正确的,因为我们的键是基于索引的,胡萝卜改变了位置。然而,您说过
msgSub
是一个昂贵的函数


地方回忆录 我注意到在你的阵列中有两次
apple

常量输入=[“苹果”、“香蕉”、“樱桃”、“榴莲”、“接骨木”、“苹果”]

我觉得您希望将计算结果记忆起来,这样,如果以前计算过
apple
,就不会再次计算
apple

我们可以将计算出的值存储在我们自己的记忆状态中,这样我们就可以查找消息值,看看以前是否计算过

const[localmemorization,setlocalmemorization]=useState({});
我们希望确保在
stringArray
更改时更新此LocalMemorization

React.useffect(()=>{
SetLocalMemorization(prevState=>
stringArray.reduce((存储,值)=>{
常量计算值=
prevState[值]??存储[值]??msgSub(值);
返回存储区[value]?存储区:{…存储区[value]:calculateValue};
})
);
},[stringArray]);
此行
const calculateValue=prevState[value]??存储[值]??msgSub(值)

  • 检查previous状态以查看它是否具有previous值(对于carrot将第一个数组移动到第二个数组的情况)
  • 检查当前存储以查看其是否具有值(对于第二个存储)
    const output = stringArray.map((msg, key) => {
        const expensiveMessage = localMemoization[msg];
        return (
          <Item
            key={key}
            message={msg}
            calculatedValue={expensiveMessage}
            index={key}
          />
        );
      });