Scott

http请求那些事 a year ago

react
4908个字符
共有144人围观

对象初始化

来看一个demo:

如果我们运行代码,大概率是会出错的,因为IDE已经标红了 😁

果不其然,出错了 无法读取属性title

作为一个合格的coder,就算IDE不给提示我们也要知道为什么。

这与react生命周期相关

组件在render下面这段html代码时,useEffect()(相当于上图的componentDidMount())还未执行,此时的post为null

   <>
      <h3>{post.title}</h3>
      <div>{post.body}</div>
    </>

null下面是没有title属性的,所以报错

那么如何fix这个bug呢?对象后面加个?即可

来看看完整的demo:

import { memo, useEffect, useState } from 'react'

type Post = {
  title: string;
  body: string;
}

const App = memo(() => {
  const [post, setPost] = useState<Post | null>(null)
  useEffect(() => {
    fetch('https://dummyjson.com/posts/1')
      .then(res => res.json())
      .then(data => setPost(data));
  }, [])
  return (
    <>
      <h3>{post?.title}</h3>
      <div>{post?.body}</div>
    </>
  )
})

export default App

接下来加点需求,给demo添加一个loading效果

import { memo, useEffect, useState } from 'react'

type Post = {
  title: string;
  body: string;
}

const App = memo(() => {
  const [post, setPost] = useState<Post | null>(null)
  const [loading, setloading] = useState<boolean>(true)

  useEffect(() => {
    fetch('https://dummyjson.com/posts/1')
      .then(res => res.json())
      .then(data => {
        setPost(data);
        setloading(false)
      })
  }, [])
  return (
    <>
      {
        loading ? <div>loading...</div> : (<>
          <h3>{post?.title}</h3>
          <div>{post?.body}</div>
        </>)
      }
    </>
  )
})

export default App

继续改变需求,通过点击按钮随机获取post

import { memo, useEffect, useState } from 'react'

type Post = {
  title: string;
  body: string;
}

const App = memo(() => {
  const [id, setId] = useState<number>(1)
  return <>
    <button onClick={() => setId(Math.floor(Math.random() * 100))}>随机获取post</button>
    <DisplayPost id={id} />
  </>
})

export default App


const DisplayPost = memo(({ id }: { id: number }) => {
  const [post, setPost] = useState<Post | null>(null)
  const [loading, setloading] = useState<boolean>(true)

  useEffect(() => {
    fetch(`https://dummyjson.com/posts/${id}`)
      .then(res => res.json())
      .then(data => {
        setPost(data);
        setloading(false)
      })
  }, [id])
  return (
    <>
      {
        loading ? <div>loading...</div> : (<>
          <h3>{post?.title}</h3>
          <div>{post?.body}</div>
        </>)
      }
    </>
  )
})

上述demo已经满足了我们的需求,但是有个潜在的隐患

如果某一时刻有大量狂点狂人在那click,那么势必会“连累”服务器,所以我们需要在客户端作一些限流处理

让浏览器主动丢弃一些请求

添加AbortController,对客户端限流,减轻服务端压力

关于AbortController的使用, 可以参考这篇博客

import { memo, useEffect, useState } from 'react'

type Post = {
  title: string;
  body: string;
}

const App = memo(() => {
  const [id, setId] = useState<number>(1)
  return <>
    <button onClick={() => setId(Math.floor(Math.random() * 100))}>随机获取post</button>
    <DisplayPost id={id} />
  </>
})

export default App


const DisplayPost = memo(({ id }: { id: number }) => {
  const [post, setPost] = useState<Post | null>(null)
  const [loading, setloading] = useState<boolean>(true)

  useEffect(() => {
    const controller = new AbortController()
    fetch(`https://dummyjson.com/posts/${id}`, {
      signal: controller.signal
    })
      .then(res => res.json())
      .then(data => {
        setPost(data);
        setloading(false)
      })
    return () => {
      controller.abort()
    }
  }, [id])
  return (
    <>
      {
        loading ? <div>loading...</div> : (<>
          <h3>{post?.title}</h3>
          <div>{post?.body}</div>
        </>)
      }
    </>
  )
})

我们再来狂点试试

错误处理

import { memo, useEffect, useState } from 'react'

type Post = {
  title: string;
  body: string;
}

const App = memo(() => {
  const [id, setId] = useState<number>(1)
  return <>
    <button onClick={() => setId(Math.floor(Math.random() * 100))}>随机获取post</button>
    <DisplayPost id={id} />
  </>
})

export default App


const DisplayPost = memo(({ id }: { id: number }) => {
  const [post, setPost] = useState<Post | null>(null)
  const [loading, setloading] = useState<boolean>(true)
  const [err, setErr] = useState(null)

  useEffect(() => {
    const controller = new AbortController()
    fetch(`https://dummyjson.com/posts1/${id}`, {
      signal: controller.signal
    }).then(
      res => {
        if (!res.ok) {
          throw Error("failed to fetch the data from the resource")
        }
        return res.json()
      }
    )
      .then(data => {
        setPost(data);
        setloading(false)
        setErr(null)
      }).catch(err => {
        setloading(false)
        setErr(err?.message)
      }
      )
    return () => {
      controller.abort()
    }
  }, [id])
  return (
    <>
      {err && <div>{err}</div>}
      {
        loading ? <div>loading...</div> : (<>
          <h3>{post?.title}</h3>
          <div>{post?.body}</div>
        </>)
      }
    </>
  )
})