简体   繁体   中英

How to fetch data when state changes in React using useSWR?

I have a use case where you can select users from a select menu, and when you do the app will display the data of the selected user.

When the page loads I get the data of the first user in the array allUsers?.[0]:

const [userSelected, setUserSelected] = useState(allUsers?.[0])

The issue I'm having is, when I select another user from the select menu, the data does not get fetched.

I'm using useSWR to fetch the data and below is my ManageUsers component where I defined useSWR and the fetchers:

export default function ManageUsers() {
  const fetcher = (url) => fetch(url).then((res) => res.json())

  const { data: allUsers, error: allUsersError } = useSWR(
    "/api/getUsers",
    fetcher
  )

  const [userSelected, setUserSelected] = useState(allUsers?.[0])

  const fetchUserByUserId = async (url) =>
    await fetch(url, {
      method: "PUT",
      body: JSON.stringify({
        userId: userSelected?.userId,
      }),
      headers: {
        "Content-Type": "application/json",
      },
    }).then((res) => res.json())

  const { data: userById, error: userByIdError } = useSWR(
    "/api/getUserByUserId",
    fetchUserByUserId
  )

  useEffect(() => {
    if (userSelected) {
      fetchHoursByUserId(
        `/api/getUserByUserId?userId=${userSelected.userId}`
      )
    }
  }, [userSelected])

return (<>...</>)
}

Can you spot what I'm doing wrong?

This error may be occurring, because your fetcher functions are being redefined when a user is selected, since your userSelected state changes. I believe this messes up useSWR auto fetching because you call a different function every time after the component mounts, and useSWR may be unable to map these with it's cached data. It appears this is how it natively works .

In useSWR's own example documentation , the fetcher function is always defined outside the scope of the component to prevent this.

Try re-factoring your code, so that your fetcher functions are pure functions, where every variable they need can be provided in the parameters, and they can be defined outside the functional component. For example fetchUsersById can be defined like so:

  const fetchUserByUserId = async (url, userSelected) =>
    await fetch(url, {
      method: "PUT",
      body: JSON.stringify({
        userId: userSelected?.userId,
      }),
      headers: {
        "Content-Type": "application/json",
      },
    }).then((res) => res.json())

And then define them outside the scope of the component. That way, when you call them, its always the same instance of the function being called.

You can then take a look at this documentation to supply your fetcher function multiple arguments.

Let me know if this resolves the issue.

Note I also noticed the following:

  useEffect(() => {
    if (userSelected) {
      fetchHoursByHairdresserId(
        `/api/getUserByUserId?userId=${userSelected.userId}`
      )
    }
  }, [userSelected])

Here you call fetchHoursByHairdresserId , but I don't see this function defined. If this is a typo, this also might be the source of your woes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM