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



我正在重构我们的一些组件,所以我正在尝试合并记忆,因为某些组件可能会使用相同的值重新呈现(例如,热链接的图像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>
);
}

然后是头像图像

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!
}

但是控制台.log仍然每次都显示。我显然在这里遗漏了一些东西,或者不太明白这是如何工作的。这个组件向下几级,但如果它每次都可用,它会在 img 中传递,所以我希望它知道如果 img 在上一个渲染中传递并且它是一样的,它知道不会再次重新渲染它,但由于某种原因它确实如此?

谢谢大家。非常感谢。

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

更多信息:

  • https://reactjs.org/docs/strict-mode.html
  • 由于严格模式,我的 React 组件渲染了两次

我遇到了同样的问题,并花了很多时间弄清楚。我也试过这个:

export default React.memo(Component, () => {console.log("Memo check"); return true;}

这不应该刷新包装的组件,并在控制台中记录"备忘录检查"。

结果:它从未显示任何内容!这表明Component总是第一次呈现,因此记忆不起作用。

此类问题的根源通常位于组件层次结构中。

溶液:

就我而言,在其中一个父组件中,我有这样的东西:

const SubComponent = () => (
<Component /> // it was actually a parent component of the Component, so less obvious to see..
)
...
return (<div>
...
<SubComponent/>
...
</div>);

这实际上是在每次刷新父组件时定义一个新的"子组件",并"第一次"显示它。

我只是简单地改变了这个:

<SubComponent/>

对此:

{SubComponent()}

它解决了问题!

事实上,这与以前的代码不同,因为它只是对返回JSX组件的函数的调用,而第一个是每次都定义一个新的子组件组件。

希望这有帮助。

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

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>

如果组件实际引用了更改的道具或函数,则备忘录不会阻止重新渲染。

在您的场景中,您的头像图像引用了 img,在这种情况下,如果父级的状态的 img 发生更改,则您的组件将被重新渲染。 或者,如果你的父级只是更换了其他道具而不是img,那么头像图像将不会重新渲染

或者,如果有任何道具,但您没有将备忘录添加到 AvatarImage,那么 AvatarImage 将针对每个父级的状态更新重新渲染。

你也需要记住img道具。

const CardHeader = props => {
const { showAvatar, img } = props;
const updatedIMG = React.useMemo(() => img, []);
return (
<CardHeader>
<ListItem>
{showAvatar && <AvatarImage img={updatedIMG} />
</ListItem>
</CardHeader>
);
}

上面的一个会起作用

相关内容

  • 没有找到相关文章

最新更新