React Hooks & redux вызывают ненужный рендеринг и некоторые неожиданные эффекты - PullRequest
0 голосов
/ 08 ноября 2019

Ожидаемое поведение - я загружаю фотообъект и отображаю его в своем компоненте. С помощью приведенного ниже кода я достигаю результата, но с некоторыми неожиданными эффектами, которые в итоге делают этот компонент бесполезным - см. Дальнейшее описание в двух частях. Я действительно хочу погрузиться в причину всего этого и понять, как я могу предотвратить такое поведение в моем приложении. Я попробовал некоторые предложения от других людей, похожих проблем, но ничего не помогло. Я не буду перечислять здесь то, что я пробовал, потому что, очевидно, каждый раз, когда я пытался - я делал что-то не так, потому что это не помогало мне. Буду благодарен за любые идеи и предложения - я что-то упускаю и не могу понять, что это.

Часть 1 проблемы. При загрузке этого компонента в первый раз и / или обновлении - я получаю несколько повторных обращений. Из Redux DevTools я могу заметить, что действия запускаются два раза, запись в консоль любого полученного из фото значения показывает, что это значение появляется в консоли 6 раз (первые 3 раза - с исходным состоянием из хранилища redux, после -с ожидаемым извлеченным из значения объекта фото).

Часть 2 проблемы. Когда я открываю следующую фотографию (тот же компонент, просто пропуская другой match.params.id) - компонент начинает перерисовываться, по-видимому, в случайное время. Для завершения этого цикла рендеринга может потребоваться несколько секунд, поэтому он рендерится иногда на десятки, иногда более 100 раз, но всегда в конце выдает необходимую информацию. Анализируя логи, я увидел, что значения выбранной фотографии теперь просто переключаются в цикле со значениями фотографии, выбранной ранее. Цикл останавливается с правильными значениями. А откуда из предыдущих значений приходят значения - я не могу понять, потому что перед извлечением нового фотообъекта я очищаю все данные предыдущего в избыточном состоянии.

Компонент:

//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 голосов
/ 08 ноября 2019

Ваша проблема в том, что вы добавляете getPhotoById в качестве зависимости вашего хука. См. эту статью о массиве зависимостей.

Если вы хотите предотвратить повторный рендеринг, вы можетевыполните следующие действия:

const ref = useRef();




getPhotoByIdRef.current = getPhotoById

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