开发者问题收集

useMemo 钩子无法与地图元素一起使用

2021-09-24
5736

我正在使用 useMemo 钩子来渲染地图项目。我向 useMemo 钩子添加了 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);

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

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

2个回答

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

  • key 应该在数组迭代中传递给父元素 - 在您的例子中, renderItems 应该将 key 传递给 div 元素
  • 您在更新 items 数组之前关闭了 loading 状态,切换两个 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);

更新

此 codesandbox 显示,仅当 items 值为按照预期进行更改。

在此处输入图片描述

Kia Kaha
2021-09-27

useCustomHook:

import { useEffect, useRef } from "react"

export default function useUpdateEffect(callback, dependencies) {
  const firstRenderRef = useRef(true)

  useEffect(() => {
    if (firstRenderRef.current) {
      firstRenderRef.current = false
      return
    }
    return callback()
  }, dependencies)
}

在您的项目中创建这些自定义钩子并使用它们。它将防止您的第一次调用问题。

Krushnasinh Jadeja
2021-10-02