React.memo 不起作用 - 我错过了啥?

Posted

技术标签:

【中文标题】React.memo 不起作用 - 我错过了啥?【英文标题】:React.memo isn't working - what am I missing?React.memo 不起作用 - 我错过了什么? 【发布时间】:2020-09-14 10:07:27 【问题描述】:

我正在重构我们的一些组件,因此我正在尝试合并记忆,因为某些组件可能使用相同的值重新呈现(例如,热链接的图像 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 在之前的渲染中被传递,并且它知道不再重新渲染它是一样的但由于某种原因,它确实如此?

谢谢大家。非常感谢。

【问题讨论】:

1. img 是字符串吗? 2. showAvatar 是否有机会在这些渲染之间发生变化? 是的,img 是一个字符串(“https://.........jpg”)。在这种情况下,显示头像始终为真。对不起,我应该提到这一点。我会更新的。 能否展示一下 CardHeader 是如何使用的以及图像值是如何传递给它的? 好像信息丢失了,这个bug现在不在我们面前。如果你可以用codesandbox.io 或fiddle 重现,那就太好了 @user.io 你找到原因了吗?现在面临同样的问题。所有道具都保持不变,React.memo 无论如何都会重新渲染。 【参考方案1】:

好吧,要么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 />


const MemoAvatarToggle = memo((src) => 
console.log('memo avatar with \'expression &&\' render');
  return <div>
  src ? <img src=src /> : <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>

【讨论】:

【参考方案2】:

您是否启用了StrictMode?这将导致使用React.memo 记忆的组件渲染两次。

更多信息:

https://reactjs.org/docs/strict-mode.html My React Component is rendering twice because of Strict Mode

【讨论】:

以上是关于React.memo 不起作用 - 我错过了啥?的主要内容,如果未能解决你的问题,请参考以下文章

用 puppeteer 更改 IP 地址似乎不起作用。我错过了啥吗?

使用 onUpgrade 不起作用,我错过了啥吗?谢谢

添加了 Springfox Swagger-UI 但它不起作用,我错过了啥?

在 Flexbox 中居中不起作用。啥是高度、宽度、显示和位置要求?我错过了啥? [复制]

我错过了啥? Flexbox 不在此代码段中将容器居中 [重复]

Livereload 无法使用 Gulp 在 Chrome 中运行,我错过了啥