Home > Software design >  React : Cycle life problem expected / redirect problem
React : Cycle life problem expected / redirect problem

Time:09-02

For my first project with React, i tried to use useContext() for call my data mocked. But after that, i have a problem.

The problem : You go to a housing page, you change the apartment ID in the URL with a Dummy ID and you update it. Great! The redirection to the 404 is done. So you go back to the home page by clicking on the link of the 404. You go back to any housing page => the 404 is always present. You repeat, you then return to the home page and you click again on a housing card and WOW the problem no longer exists.

My Provider

import React, { useState, createContext } from "react"
import fetchLocationData from "../../services/localFetch"

export const FetchDataContext = createContext()

export const FetchDataProvider = ({ children }) => {
  const [locationData, setLocationData] = useState({})
  const [locationsData, setLocationsData] = useState([])
  const [allLocationLoading, setAllLocationLoading] = useState(false)
  const [isLocationLoading, setIsLocationLoading] = useState(false)
  const [errorAPI, setErrorAPI] = useState(false)
  const [error404, setError404] = useState(false)

  async function fetchLocationById(locId) {
    try {
      setError404(false)
      const response = await fetchLocationData.getLocById(locId)
      response ? setLocationData(response) : setError404(true)
    } catch (err) {
      console.log(err)
      setErrorAPI(true)
    } finally {
      setIsLocationLoading(true)
      console.log("in provider :", error404)
    }
  }

  async function fetchAllLocations() {
    try {
      const response = await fetchLocationData.getAll()
      setLocationsData(response)
    } catch (err) {
      console.log(err)
      setErrorAPI(true)
    } finally {
      setAllLocationLoading(true)
    }
  }

  return (
    <FetchDataContext.Provider
      value={{
        errorAPI,
        error404,
        isLocationLoading,
        allLocationLoading,
        locationData,
        locationsData,
        fetchLocationById,
        fetchAllLocations,
      }}
    >
      {children}
    </FetchDataContext.Provider>
  )
}

My router

function App() {
  return (
    <>
      <GlobalStyle />
      <BlocPage>
        <Header />
        <FetchDataProvider>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/apartment/:locId" element={<ProfileLocation />} />
            <Route path="*" element={<Error />} />
          </Routes>
        </FetchDataProvider>
      </BlocPage>
      <Footer />
    </>
  )
}

My housing page

function ProfileLocation() {
  const { locId } = useParams()
 
  const {
    errorAPI,
    error404,
    locationData,
    isLocationLoading,
    fetchLocationById,
  } = useContext(FetchDataContext)

  useEffect(() => {
    fetchLocationById(locId)
  }, [])

  const rating = parseInt(locationData.rating)

  if (errorAPI) {
    return (
      <span>
        Oups une erreur est survenue ... Veuillez recommencer ultérieurement.
      </span>
    )
  }

  if (error404) {
    return <Navigate to="/error404" />
  }
 
  return (
    <div>
      {isLocationLoading ? (
        <>
          <Slider pictures={locationData.pictures} />
          <ResponsiveWrapper>
            <FirstSectionWrapper>
              <Title>{locationData.title}</Title>
              <Geolocation>{locationData.location}</Geolocation>
              <TagsWrapper>
                {locationData.tags.map((tag, index) => (
                  // eslint-disable-next-line react/no-array-index-key
                  <Tag key={`${tag}-${index}`} label={tag} />
                ))}
              </TagsWrapper>
            </FirstSectionWrapper>

            <SecondSectionWrapper>
              <OwnerWrapper>
                <HomeOwnerName>{locationData.host.name}</HomeOwnerName>
                <HomeOwerPicture
                  src={locationData.host.picture}
                  alt={locationData.host.name}
                />
              </OwnerWrapper>
              <StarsWrapper>
                <StarScale ratingValue={rating} starType="full" />
                <StarScale ratingValue={rating} starType="empty" />
              </StarsWrapper>
            </SecondSectionWrapper>
          </ResponsiveWrapper>

          <CollapseSectionWrapper>
            <CollapseWrapper>
              <Collapse
                pageType="profil"
                label="Description"
                contentType="paragraph"
                contentText={locationData.description}
              />
            </CollapseWrapper>
            <CollapseWrapper>
              <Collapse
                pageType="profil"
                label="Équipements"
                contentType="list"
                contentText={locationData.equipments}
              />
            </CollapseWrapper>
          </CollapseSectionWrapper>
        </>
      ) : (
        <Loader />
      )}
    </div>
  )
}

export default ProfileLocation

Maybe it’s an async code problem?

CodePudding user response:

The issue is that only navigating forward to a route rendering a component that calls fetchLocationById will ever manage to clear/set the error404 state back to false.

I suggest instead of passing error404 back to the components that the FetchDataProvider handles rendering the error UI upon the error condition. Having it redirect to a specific error page.

Example:

<FetchDataProvider>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/about" element={<About />} />
    <Route path="/apartment/:locId" element={<ProfileLocation />} />
    <Route path="/404" element={<Error404 />} /> // <-- add route
    <Route path="*" element={<Error />} />
  </Routes>
</FetchDataProvider>

...

const Error404 = () => (
  <div>Oups une erreur est survenue ... Veuillez recommencer ultérieurement.</div>
);

...

import { useNavigate } from 'react-router-dom';

export const FetchDataProvider = ({ children }) => {
  const navigate = useNavigate();

  ...

  async function fetchLocationById(locId) {
    try {
      setError404(false)
      const response = await fetchLocationData.getLocById(locId)
      if (response) {
        setLocationData(response);
      } else {
        navigate("/error404", { replace: true });
      }
    } catch (err) {
      navigate("/error404", { replace: true });
    } finally {
      setIsLocationLoading(true);
    }
  }

  ...

  return (
    ...
  );
}

...

function ProfileLocation() {
  const { locId } = useParams();
 
  const {
    locationData,
    isLocationLoading,
    fetchLocationById,
  } = useContext(FetchDataContext);

  useEffect(() => {
    fetchLocationById(locId);
  }, [fetchLocationById, locId]); // <-- don't forget to add proper dependencies

  const rating = parseInt(locationData.rating);

  return (
    <div>
      {isLocationLoading ? (
        <>
          <Slider pictures={locationData.pictures} />
          <ResponsiveWrapper>
            <FirstSectionWrapper>
              <Title>{locationData.title}</Title>
              <Geolocation>{locationData.location}</Geolocation>
              <TagsWrapper>
                {locationData.tags.map((tag, index) => (
                  // eslint-disable-next-line react/no-array-index-key
                  <Tag key={`${tag}-${index}`} label={tag} />
                ))}
              </TagsWrapper>
            </FirstSectionWrapper>

            <SecondSectionWrapper>
              <OwnerWrapper>
                <HomeOwnerName>{locationData.host.name}</HomeOwnerName>
                <HomeOwerPicture
                  src={locationData.host.picture}
                  alt={locationData.host.name}
                />
              </OwnerWrapper>
              <StarsWrapper>
                <StarScale ratingValue={rating} starType="full" />
                <StarScale ratingValue={rating} starType="empty" />
              </StarsWrapper>
            </SecondSectionWrapper>
          </ResponsiveWrapper>

          <CollapseSectionWrapper>
            <CollapseWrapper>
              <Collapse
                pageType="profil"
                label="Description"
                contentType="paragraph"
                contentText={locationData.description}
              />
            </CollapseWrapper>
            <CollapseWrapper>
              <Collapse
                pageType="profil"
                label="Équipements"
                contentType="list"
                contentText={locationData.equipments}
              />
            </CollapseWrapper>
          </CollapseSectionWrapper>
        </>
      ) : (
        <Loader />
      )}
    </div>
  );
}
  • Related