我正在重构我们的一些组件,所以我正在尝试合并记忆,因为某些组件可能会使用相同的值重新呈现(例如,热链接的图像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
并不总是正确的,要么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>
如果组件实际引用了更改的道具或函数,则备忘录不会阻止重新渲染。
在您的场景中,您的头像图像引用了 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>
);
}
上面的一个会起作用