6

我正在重构我们的一些组件,因此我正在尝试合并 memoization,因为某些组件可能会使用相同的值重新呈现(例如,除非它们相同,否则热链接的图像 URL)。

我有一个简单的组件:

const CardHeader = props => {
    // img is a stringand showAvatar is a boolean but it's always true
    const { ..., showAvatar, img } = props;

    return (
        <CardHeader>
            <ListItem>
                // AvatarImage shouldn't re-render if img is the same as previous
                {showAvatar && <AvatarImage img={img} />
            </ListItem>
        </CardHeader>
    );
}

然后是AvatarImage

const AvatarImage = React.memo(props => {
   console.log("why is this still re-rendering when the img value hasn't changed?");
   const { img } = props;

   return (
        <ListItemAvatar>
            {img ?
                <Avatar src={img} />    
                :
                <Avatar>
                    Some initials
                </Avatar>
            }
        </ListItemAvatar>
    );
});

我也试过传入备忘录的第二个参数:

(prevProps, nextProps) => {
    return true; // Don't re-render!
}

但是 console.log 仍然每次都显示。我显然在这里遗漏了一些东西,或者不太明白这是如何工作的。这个组件低了几个级别,但是如果它每次都可用,它就会传入 img,所以我希望它知道 img 是否在上一次渲染中被传递,并且它知道不再重新渲染它是相同的但出于某种原因,它会吗?

谢谢大家。非常感谢。

4

2 回答 2

0

StrictMode启用了吗?这将导致一个被记忆的组件React.memo渲染两次。

更多信息:

于 2021-10-08T19:22:29.227 回答
0

好吧,要么showAvatar并不总是正确,要么CardHeader ListItem组件神奇地决定是否显示孩子

例子

const { useState, useEffect, memo, createContext, useContext } = React;

const getAvatars = () => Promise.resolve([
{
  src: 'https://i.picsum.photos/id/614/50/50.jpg'
},
{
  src: 'https://i.picsum.photos/id/613/50/50.jpg'
}
])

const Avatar = ({src}) => {
console.log('avatar render');
  return <img src={src} alt="avatar"/>
}

const MemoAvatarToggle = memo(({src}) => {
console.log('memo avatar with \'expression &&\' render');
  return <div>
  {src ? <img src={src} alt="avatar"/> : <div>Test </div>}
  </div>
})

const CardHeader = ({children}) => {
  const luck = Boolean(Math.floor(Math.random() * 1.7));
  
  
  
  return <div>
    {luck && children}
  </div>
}

const ListItem = ({children}) => {
  return <div>
    {children}
  </div>
}

const ShowAvatarContext = createContext()

const App = (props) => {
  const [avatars, setAvatars] = useState([]);
  const [toggle, setToggle] = useState(false);
  const [showAvatar, setShowAvatar] = useContext(ShowAvatarContext);
  
  useEffect(() => {
    let isUnmounted = false;
    let handle = null;
    
    setTimeout(() => {
      if(isUnmounted) {
        return;
      }
      setShowAvatar(true);
    }, 500);
    
    getAvatars()
      .then(avatars => {
        if(isUnmounted) {
          return;
        }
        
        setAvatars(avatars)
      })
    
    const toggle = () => {
      setToggle(prev => !prev);
      handle = setTimeout(toggle, 1000);
      //setShowAvatar(prev => !prev);
    }
    
    handle = setTimeout(toggle, 1000);
    
    return () => {
      isUnmounted = true;
      clearTimeout(handle);
    }
      
  }, []);
 
  return <div>
    <CardHeader>
      <ListItem>
        {showAvatar && avatars.map((avatar, index) => <MemoAvatarToggle key={index} src={avatar.src}/>)}
      </ListItem>
    </CardHeader>
    {toggle ? 1 : 0} 
  </div>
}

const ShowAvatarProvider = ({children}) => {
  const state = useState(false);
  
  return <ShowAvatarContext.Provider value={state}>
      {children}
    </ShowAvatarContext.Provider>
}

ReactDOM.render(
    <ShowAvatarProvider>
        <App/>
    </ShowAvatarProvider>,
    document.getElementById('root')
  );
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>

于 2020-05-27T12:46:46.127 回答