0

我正在为我的 React 应用程序的性能问题而苦苦挣扎。例如,我有一张卡片列表,您可以在 Facebook 上添加类似的卡片。一切,一旦其中一个孩子更新,所有列表都会重新呈现,所以在这里我尝试使用 useMemo 或 React.memo。我以为我可以将 React.memo 用于卡片组件,但没有成功。不知道我是否错过了一些重要的部分..

父.js

const Parent = () => {
 const postLike= usePostLike()
 const listData = useRecoilValue(getCardList)
 // listData looks like this -> 
 //[{ id:1, name: "Rose", avararImg: "url", image: "url", bodyText: "text", liked: false, likedNum: 1, ...etc }, 
 // { id:2, name: "Helena", avararImg: "url", image: "url", bodyText: "text", liked: false, likedNum: 1, ...etc },
 // { id: 3, name: "Gutsy", avararImg: "url", image: "url", bodyText: "text", liked: false, likedNum: 1, ...etc }]

  const memoizedListData = useMemo(() => {
    return listData.map(data => {
      return data
    })
  }, [listData])

  return (
    <Wrapper>
      {memoizedListData.map(data => {
        return (
          <Child
            key={data.id}
            data={data}
            postLike={postLike}
          />
        )
      })}
    </Wrapper>
  )
}
export default Parent

使用PostLike.js

export const usePressLike = () => {
   const toggleIsSending = useSetRecoilState(isSendingLike)
    const setList = useSetRecoilState(getCardList)

  const asyncCurrentData = useRecoilCallback(
    ({ snapshot }) =>
      async () => {
        const data = await snapshot.getPromise(getCardList)
        return data
      }
  )

  const pressLike = useCallback(
    async (id) => {
      toggleIsSending(true)
      const currentList = await asyncCurrentData()

       ...some api calls but ignore now
       const response = await fetch(url, {
        ...blabla
      })

        if (currentList.length !== 0) {
          const newList = currentList.map(list => {
            if (id === list.id) {
              return {
                ...list,
                liked: true,
                likedNum: list.likedNum + 1,
              }
            }
            return list
          })
          setList(newList)
        }           
       toggleIsSending(false)
      }
    },
    [setList, sendYell]
  )

  return pressLike
}

Child.js


const Child = ({
 postLike,
 data 
}) => {

  const { id, name, avatarImg, image, bodyText, likedNum, liked } = data;

  const onClickPostLike = useCallback(() => {
    postLike(id)
  })

  return (
     <Card>
     // This is Material UI component
     <CardHeader
        avatar={<StyledAvatar src={avatarImg}  />}
        title={name}
         subheader={<SomeImage />} 
      />
     <Image drc={image} />
     <div>{bodyText}</div>
     <LikeButton
        onClickPostLike={onClickPostLike}
        liked={liked}
        likedNum={likedNum}
      /> 
     </Card>
  )
}


export default Child

LikeButton.js


const LikeButton = ({ onClickPostLike, like, likedNum }) => {
  const isSending = useRecoilValue(isSendingLike)

  return (
    <Button
      onClick={() => {
        if (isSending) return;
        onClickPostLike()
      }}
    >
      {liked ? <ColoredLikeIcon /> : <UnColoredLikeIcon />}
     <span> {likedNum} </span>
    </Button>
  )
}

export default LikeButton


这里的主要问题是,当其中一个列表更新时,使用备忘录的最佳方式是什么。记住父组件中的整个列表或每个子列表,或者在子组件中使用 React.memo ......(但想象一下,如果用户编辑它们,其他事情也会改变。例如文本,图像......)我总是看到父组件使用 React 开发工具突出显示。

4

1 回答 1

0

在子组件中使用 React.memo

您可以这样做并提供自定义比较器功能:


const Child = React.memo(
  ({
    postLike,
    data 
  }) => {...},
  (prevProps, nextProps) => prevProps.data.liked === nextProps.data.liked
);

您当前的使用useMemo不会做任何事情。useMemo当您的组件有其他状态更新并且您需要计算一个昂贵的值时,您可以将其用作性能优化。假设您有一个显示列表的可折叠面板:


const [expand, setExpand] = useState(true);
const serverData = useData();
const transformedData = useMemo(() => 
  transformData(serverData),
[serverData]);

return (...);

useMemo这样您就不会在serverData每次用户展开/折叠面板时重新转换。

请注意,如果您自己在效果中进行获取,这是一个人为的示例,但它确实适用于一些常见的库,如 React Apollo。

于 2021-09-24T21:25:46.180 回答