useMemo 挂钩不适用于地图元素

sky*_*ine 6 reactjs react-hooks react-usememo

我正在使用 useMemo 挂钩来渲染地图项目。我向 useMemo 挂钩添加了 items 参数,根据项目更改它将呈现。但是改变加载状态和items发生变化,Item自定义组件会渲染两次。我在使用 useMemo 钩子时是否犯了任何错误,请纠正我。

Home:

import React, { useState, useEffect, useMemo } from "react";
import Item from "./Item";

const array = [1];
const newArray = [4];

const Home = () => {
  const [items, setItems] = useState(array);
  const [loading, setLoading] = useState(false);
  const [dataChange, setDataChange] = useState(1);

  const renderItems = (item, index) => {
    return (
      <div key={item}>
        <Item id={item}></Item>
      </div>
    );
  };
  useEffect(() => {
    if (dataChange === 2) {
      setLoading(true);
      setTimeout(() => {
        setLoading(false);
        setItems(newArray);
      }, 3000);
    }
  }, [dataChange]);

  const memoData = useMemo(() => {
    return <div>{items.map(renderItems)}</div>;
  }, [items]);

  return (
    <div style={{ display: "flex", flexDirection: "column" }}>
      <input
        onClick={() => {
          setDataChange(2);
        }}
        style={{ height: 40, width: 100, margin: 20 }}
        type="button"
        value="ChangeItem"
      ></input>
      <div>{loading ? <label>{"Loading"}</label> : <div>{memoData}</div>}</div>
    </div>
  );
};
export default React.memo(Home);

Item:

import React,{useEffect} from "react";
const Item = (props) => {
  console.log("props", props);
useEffect(() => {
// call api with props.id
 }, [props]);
  return <div>Hello world {props.id}</div>;
};
export default React.memo(Item);
Run Code Online (Sandbox Code Playgroud)

结果: 第一次: props {id: 1}

点击后: props {id: 1} props {id: 4}

Kia*_*aha 8

上面的代码中有一些不正确的地方。

  • key应该在数组迭代中传递给父元素 - 在您的情况下应该将renderItems传递keydiv元素
  • loading您在更新数组之前关闭状态items,切换两个setState表达式将在大多数情况下解决您的情况,尽管setState这是一个异步函数并且不能保证
  • 如果常量或函数与组件的状态没有紧密耦合,那么最好将其提取到组件外部,就像这样renderItems

这就是为什么又执行了一个console.log 在此输入图像描述

  • 还应该记住,记忆需要时间,并且您希望尽可能保持高效,因此您可以完全跳过处理数组的组件,因为它保持在状态中,并且它的引用不会useMemo改变React.memo如果状态保持不变则重新渲染
    const array = [1];
    const newArray = [4];
    
    const Home = () => {
      const [items, setItems] = useState(array);
      const [loading, setLoading] = useState(false);
      const [dataChange, setDataChange] = useState(1);
    
      useEffect(() => {
        if (dataChange === 2) {
          setLoading(true);
          setTimeout(() => {
            setItems(newArray);
            setLoading(false);
          }, 3000);
        }
      }, [dataChange]);
    
      return (
        <div style={{ display: "flex", flexDirection: "column" }}>
          <input
            onClick={() => {
              setDataChange(2);
            }}
            style={{ height: 40, width: 100, margin: 20 }}
            type="button"
            value="ChangeItem"
          ></input>
          <div>
            {loading ? <label>{"Loading"}</label> : <ItemsMemo items={items} />}
          </div>
        </div>
      );
    };
    
    const renderItems = (item) => {
      return (
        <span key={item} id={item}>
          {item}
        </span>
      );
    };
    
    const Items = ({ items }) => {
      console.log({ props: items[0] });
    
      return (
        <div>
          Hello world <span>{items.map(renderItems)}</span>
        </div>
      );
    };
    
    const ItemsMemo = React.memo(Items);
Run Code Online (Sandbox Code Playgroud)

更新

此codesandbox显示useMemo仅当items值按预期发生变化时才会被调用。

在此输入图像描述