在渲染之前重置 React 状态

时间:2021-04-26 22:54:21

标签: javascript reactjs react-router react-hooks jsx

我正在构建一个使用 TMDB Api 的 Web 应用程序。我有以下代码可以获取有关电视节目的所有信息

export const useShowInfoFetch = ({showId}) => {

  const [data, setData] = useState({})
  const [loading, setLoading] = useState(false)
  const [_error, _setError] = useState(false)

  const fetchShowInfo = useCallback(() => {
    setLoading(true)

    try {
      axios.get(getShowInfo(showId))
        .then(response => {
          setData(response.data)
        })
    } catch (error) {
      _setError(true)
    } finally {
      setLoading(false)
    }
  }, [showId])

  useEffect(() => {
    fetchShowInfo()
  }, [fetchShowInfo])

  return [data, loading, _error]
}

获取的所有信息都显示在页面中,页面中还有 Linksreact-router-dom。这些链接转到另一个电视节目页面。 问题是,当我在一个有 X 个季节数量的电视节目的页面中并且我点击一个季节较少的电视节目时,我所在页面的季节会持续一段时间。因此,当我获取每个季节的信息时,我在季节较少的页面中得到了 404。

这是错误的截图

enter image description here

橙色圆圈是它显示的内容,因为我点击了季节较少的电视节目。 如您所见,上一页中的季节持续了一段时间,并且因为外星人只有 2 个季节(不是 9 个),所以我得到了 404。您还可以注意到,后者显示了正确的季节数量。

我尝试在 useEffect 钩子中添加一个清理方法。像这样:

useEffect(() => {
    fetchShowInfo()
    
    return function cleanup() {
      setData({})
    }
  }, [fetchShowInfo])

但这没有用。

我知道我可以在 then Axios 承诺之后使用 catch 处理这个问题,但我想弄清楚为什么会发生这种情况并用一个好的方法解决这个问题解决方案而不是避免它。

欢迎任何帮助,如果需要,我可以与所有代码共享存储库。

编辑:

为了显示类似的电影,我使用了另一个自定义钩子

export const useSimilarFetch = (elementType, elementId) => {

  const [similarElements, setSimilarElements] = useState({elements: []})
  const [similarLoading, setSimilarLoading] = useState(false)
  const [_error, _setError] = useState(false)

  const fetchSimilarElements = useCallback(async (endpoint) => {
    console.log(">>> fetching similar elements <<<")
    setSimilarLoading(true)

    try {
      await axios.get(endpoint)
        .then(response => {
          setSimilarElements(() => ({
            elements: [...response.data.results],
            currentPage: response.data.page,
            totalPages: response.data.total_pages
          }))
        })
    } catch (error) {
      _setError(true)
    } finally {
      setSimilarLoading(false)
    }
  }, [])

  useEffect(() => {
    fetchSimilarElements(getSimilar(elementType, elementId));
  }, [fetchSimilarElements, elementType, elementId])

  return [{similarElements, similarLoading, _error}, fetchSimilarElements]
}

然后,在我的 ShowInfoComponent 中,我像这样调用所有需要的钩子:

const {showId} = useParams()
const [data, loading, _error] = useShowInfoFetch({showId})
const [{similarElements, similarLoading}] = useSimilarFetch("tv", showId)

谢谢。

2 个答案:

答案 0 :(得分:1)

showId 更改时,data 必须等待一个额外的渲染周期,因此 showId 已被使用,即使 data 尚未被提取。 UI 依赖于 showIddata,而 data 依赖于 showId。解决此问题的一种方法可能是让您的 UI 仅依赖于 data。身份证呢?例如,将其添加到 data。我们只是想避免不同步。

像这样:

export const useShowInfoFetch = ({showId}) => {

  const [data, setData] = useState({})
  const [loading, setLoading] = useState(false)
  const [_error, _setError] = useState(false)

  const fetchShowInfo = useCallback(() => {
    setLoading(true)

    try {
      axios.get(getShowInfo(showId))
        .then(response => {
          setData({ id: showId, info: response.data})
        })
    } catch (error) {
      _setError(true)
    } finally {
      setLoading(false)
    }
  }, [showId])

  useEffect(() => {
    fetchShowInfo()
  }, [fetchShowInfo])

  return [data, loading, _error]
}

然后使用 data.id 建立您的链接。 如果 response.data 已经包含 id,那么最好使用它。

这只是一个例子,当然,但希望你能明白。

答案 1 :(得分:0)

我可能错了,但我相信您没有看到 useEffect 的正确值。您应该关注 showId 而不是 fetchShowInfo 函数。即:

 useEffect(() => {
  fetchShowInfo()
 }, [showId]) --> HERE

当你记住回调时,如果你看错了变量,那么你会得到“最后回答”。