React Hooks和redux会导致不必要的渲染和一些意外效果

时间:2019-11-08 15:16:55

标签: reactjs redux react-hooks

预期的行为-我加载照片对象并将其渲染到组件中。 使用下面的代码,我确实可以达到预期的效果,但是却产生了一些意想不到的效果,这些效果最终使该组件不再有用-参见两部分的进一步说明。 我真的很想深入探讨所有原因,并了解如何防止我的应用中出现此类行为。 我确实尝试了其他人类似问题的一些建议,但没有任何帮助。我不会在这里列出我尝试过的事情,因为,显然,每次尝试时-我做错了什么,因为它没有帮助。 对于任何想法和建议,我将不胜感激-我错过了一些东西,无法理解它是什么。

第1部分。 首次加载和/或刷新该组件时-我得到了多个渲染器。从Redux DevTools中,我可以观察到动作触发了两次,控制台记录了从photo值收到的任何值,显示该值在控制台中出现了6次(前3次-处于redux-store的初始状态,然后-并从照片对象的值中提取了预期值。

第2部分。 当我打开下一张照片(相同的组件,只是传递不同的match.params.id)时,该组件显然开始随机渲染。完成此重新渲染循环可能需要几秒钟,因此它有时会重新渲染数十次,有时会重新渲染100次以上,但最终总是渲染所需的信息。 分析日志后,我发现现在获取的照片的值正与之前获取的照片的值在循环中切换。循环以正确的值停止。而且previos值从何而来-我无法弄清楚,因为在获取新的照片对象之前,我先清除了redux状态下的所有数据。

组件:

//IMPORTS

const Photo = ({ getPhotoById, photo, loading, match }) => {
  const [photoData, setPhotoData] = useState({
    photoID: match.params.id,
    imgUrl: '',
    photoFileName: '',
    title: '',
    description: '',
    albumID: '',
    albumName: '',
    categoryID: '',
    categoryName: '',
    categoryID2: '',
    categoryName2: '',
    categoryID3: '',
    categoryName3: '',
    locationID: '',
    locationName: '',
    contributorID: '',
    contributorName: '',
    contributorWeb: '',
    source: '',
    sourceWeb: '',
    author: '',
    periodID: '',
    periodName: '',
    license: ''
  });

  const {
    photoID,
    imgUrl,
    photoFileName,
    title,
    description,
    albumID,
    albumName,
    categoryID,
    categoryName,
    categoryID2,
    categoryName2,
    categoryID3,
    categoryName3,
    locationID,
    locationName,
    contributorID,
    contributorName,
    source,
    sourceWeb,
    author,
    periodID,
    periodName,
    license
  } = photoData;

  useEffect(() => {
    getPhotoById(photoID);
  }, [getPhotoById, photoID]);

  useEffect(() => {
    if (loading === false) {
      const {
        photoID,
        imgUrl,
        photoFileName,
        title,
        description,
        albumID,
        albumName,
        categoryID,
        categoryName,
        categoryID2,
        categoryName2,
        categoryID3,
        categoryName3,
        locationID,
        locationName,
        contributorID,
        contributorName,
        source,
        sourceWeb,
        author,
        periodID,
        periodName,
        license
      } = photo;

      setPhotoData({
        photoID,
        imgUrl,
        photoFileName,
        title,
        description,
        albumID,
        albumName,
        categoryID,
        categoryName,
        categoryID2,
        categoryName2,
        categoryID3,
        categoryName3,
        locationID,
        locationName,
        contributorID,
        contributorName,
        source,
        sourceWeb,
        author,
        periodID,
        periodName,
        license
      });
    }
  }, [loading]);

  useEffect(() => {
    if (!loading) {
      initOpenseadragon();
    }
  }, [loading]);

  console.log(photoFileName, 'photoFileName');

  const initOpenseadragon = () => {
    OpenSeadragon({
      id: 'viewer',
      tileSources: `/uploads/tiles/${photoFileName}.dzi`,
      prefixUrl: '/images/osd/',
      showZoomControl: true,
      showHomeControl: true,
      showFullPageControl: true,
      showRotationControl: true
    });
  };


  return !photo && !loading ? (
    <NotFound />
  ) : (
    <Fragment>
SOME JSX
    </Fragment>
  );

};

Photo.propTypes = {
  getPhotoById: PropTypes.func.isRequired,
  // photo: PropTypes.object.isRequired,
  loading: PropTypes.bool.isRequired
};

const mapStateToProps = state => {
  return {
    photo: state.photo.photo,
    loading: state.photo.loading
  };
};

export default connect(
  mapStateToProps,
  { getPhotoById }
)(Photo);

动作:

export const getPhotoById = photo_id => async dispatch => {
  try {
    dispatch({ type: CLEAR_PHOTO });
    dispatch({ type: LOAD_PHOTO });
    const res = await axios.get(`/api/photo/${photo_id}`);

    dispatch({
      type: GET_PHOTO,
      payload: res.data
    });
  } catch (err) {
    dispatch({
      type: PHOTOS_ERROR,
      payload: { msg: err.response.statusText, status: err.response.status }
    });
  }
};

减速机

const initialState = {
  photo: null,
  photos: [],
  loading: true,
  error: {}
};

const photo = (state = initialState, action) => {
  const { type, payload } = action;
  switch (type) {
    case GET_PHOTO:
      return {
        ...state,
        photo: payload,
        loading: false
      };
    case LOAD_PHOTO:
      return {
        ...state,
        loading: true
      };
    case CLEAR_PHOTO:
      return {
        ...state,
        photo: null,
        loading: false
      };
    case PHOTOS_ERROR:
      return {
        ...state,
        error: payload,
        loading: false
      };

    default:
      return state;
  }
};

export default photo;

1 个答案:

答案 0 :(得分:0)

您的问题是您要添加getPhotoById作为挂钩的依赖项,请参见this article有关依赖项数组。

如果要防止重新渲染,可以执行以下操作:

const ref = useRef();




getPhotoByIdRef.current = getPhotoById

useEffect(() => {
  getPhotoByIdRef(match.params.id)
}, [getPhotoByIdRef, match.params.id]);