javascript - 如何避免在 React 中重新渲染组件?
问题描述
使用 React 和 Redux 创建一个简单的应用程序。
关键是从服务器获取照片,显示它们,如果您单击带有更大照片和评论的照片显示模式窗口。
App 组件的代码
import React, { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import './App.scss'
import List from './components/list/List'
import Header from './components/header/Header'
import Footer from './components/footer/Footer'
import ModalContainer from './containers/ModalContainer'
import { getPhotos, openModal } from './redux/actions/actions'
const App = () => {
const { isFetching, error } = useSelector(({ photos }) => photos)
const photos = useSelector(({ photos }) => photos.photos)
const { isOpen } = useSelector(({ modal }) => modal)
const dispatch = useDispatch()
useEffect(() => {
dispatch(getPhotos())
}, [])
const getBigPhoto = (id) => {
dispatch(openModal(id))
}
return (
<div className="container">
<Header>Test App</Header>
<div className="list__content">
{isFetching
? <p>Loading...</p>
: error
? <p>{error}</p>
: photos.map(({ id, url }) => (
<List
key={id}
src={url}
onClick={() => getBigPhoto(id)}
/>
))
}
</div>
<Footer>© 2019-2020</Footer>
{isOpen && <ModalContainer />}
</div>
)
}
export default App
在这一行中,如果我刷新页面,我只会获取一次照片以停止重新渲染
useEffect(() => {
dispatch(getPhotos())
}, [])
当我单击照片时,我的模式打开并且我想停止重新渲染所有组件。例如对于我的标题,我像这样使用 React.memo HOC
import React, { memo } from 'react'
import './Header.scss'
import PropTypes from 'prop-types'
const Header = memo(({ children }) => {
return <div className="header">{children}</div>
})
Header.propTypes = {
children: PropTypes.string,
}
Header.defaultProps = {
children: '',
}
export default Header
当我打开和关闭我的模态时,它工作得很好。页眉和页脚不会重新呈现。但是每次我打开和关闭模式窗口时都会重新呈现 List 组件。发生这种情况是因为 List 组件中的那个道具onClick={() => getBigPhoto(id)}
在我每次单击时都会创建一个新的匿名函数。如您所知,如果您的道具发生了变化,组件将被重新渲染。
我的问题是如何避免在我的情况下重新呈现 List 组件?
解决方案
您可以为接收 getBigPhoto 和 id 的 List 创建一个容器,使用 useCallback 创建 getBigPhoto,这样函数就不会改变:
const ListContainer = React.memo(function ListContainer({
id,
src,
getBigPhoto,
}) {
return (
<List
key={id}
src={scr}
onClick={() => getBigPhoto(id)}
/>
);
});
const App = () => {
const { isFetching, error, photos } = useSelector(
({ photos }) => photos
);
const { isOpen } = useSelector(({ modal }) => modal);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getPhotos());
}, []);
//use callback so getBigPhoto doesn't change
const getBigPhoto = React.useCallback((id) => {
dispatch(openModal(id));
}, []);
return (
<div className="container">
<Header>Test App</Header>
<div className="list__content">
{isFetching ? (
<p>Loading...</p>
) : error ? (
<p>{error}</p>
) : (
photos.map(({ id, url }) => (
// render pure component ListContainer
<ListContainer
key={id}
src={url}
id={id}
getBigPhoto={getBigPhoto}
/>
))
)}
</div>
<Footer>© 2019-2020</Footer>
{isOpen && <ModalContainer />}
</div>
);
};
推荐阅读
- ffmpeg - ffmpeg strftime 对 Windows 没有影响
- node.js - Firebase 的云函数 - getaddrinfo EAI_GAIN site.com:443
- php - 在php中的几个间隔之间生成一个随机数
- wordpress - wordpress 不断重定向到已删除的页面
- sql-server - 通过链接到 SQL 服务器数据库的 MS Access 以多对多关系(中间表)插入数据
- azure-active-directory - 我是否可以在用户通过 Azure AD SSO 进行身份验证的外部网站/应用程序的 iFrame 中使用 Sharepoint 视图
- amazon-web-services - Athena 上的 ALTER TABLE ADD PARTITION 有任何限制吗?
- angular - 我已经安装了 npm install fontawesome-free,仍然给我这个错误
- gcc - 如何编译 tree-sitter 解析器?
- elasticsearch - 在具有大量存储桶的 Elastic 中导航术语聚合