简体   繁体   English

如何避免在 React 中重新渲染组件?

[英]How to avoid rerender of a component in React?

Creating a simple app using React and Redux.使用 React 和 Redux 创建一个简单的应用程序。

The point is to get photos from the server, show them and if you click on the photo show modal window with bigger photo and comments.关键是从服务器获取照片,显示它们,如果您单击带有更大照片和评论的照片显示模式窗口。

The code for App component 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>&copy; 2019-2020</Footer>
      {isOpen && <ModalContainer />}
    </div>
  )
} 

export default App

In this line I get photos only once to stop rerender if I refresh the page在这一行中,如果我刷新页面,我只会获得一次照片以停止重新渲染

  useEffect(() => {
    dispatch(getPhotos())
  }, [])

When I click on the photo my modal opens and I want to stop rerendering all the components.当我点击照片时,我的模态打开,我想停止重新渲染所有组件。 For example for my header I use React.memo HOC like this例如,对于我的标题,我像这样使用 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

It works perfectly when I open and close my modal.当我打开和关闭我的模态时,它工作得很好。 Header and Footer are not rerendered.页眉和页脚不会重新渲染。 But List component is rerendered every time I open and close a modal window.但是每次打开和关闭模态窗口时都会重新渲染 List 组件。 It's happening because that prop onClick={() => getBigPhoto(id)} in List component creates a new anonymous function every time I click.发生这种情况是因为 List 组件中的 prop onClick={() => getBigPhoto(id)}每次单击时都会创建一个新的匿名函数。 As you know if your props changed, component is rerendered.如您所知,如果 props 发生变化,组件会重新渲染。

My question is how to avoid rerender of List component in my situation?我的问题是如何避免在我的情况下重新呈现 List 组件?

You can create a container for List that receives getBigPhoto and an id, create getBigPhoto with useCallback so the function doesn't change:您可以为 List 创建一个接收 getBigPhoto 和 id 的容器,使用 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>&copy; 2019-2020</Footer>
      {isOpen && <ModalContainer />}
    </div>
  );
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM