swr icon indicating copy to clipboard operation
swr copied to clipboard

WIP: useSWRAggregator

Open promer94 opened this issue 3 years ago • 3 comments

This pr is trying to explore a possible solution for #1041 and #1988

Motivation

In most of time, if users want to render a list of useSWR hooks, they could do something like this.

const Item = ({ swrKey }) => {
  const { data } = useSWR(swrKey, fetcher)
  return <div>{data ? data : 'loading'}</div>
}
const Demo = () => {
  const [keys] = useState(['/api/data?id=0', '/api/data?id=1', '/api/data?id=2'])
  return <div>{keys.map(v => <Item key={v} swrKey={v} />)}</div>
}

But this pattern has some problems

  • too much boilerplate code.
  • user has to create key for their item component. comment
  • user could not access aggregate result of swr hooks. comment

useSWRAggregator

Design

  • using render props to reduce boilerplate code
  • key for item component will be created by swr's build-in serialization
  • user can access aggregate result from the result of useSWRAggregator and render props

Usage

  • Simple
const Demo = () => {
  const { data } = useSWRAggregator(keys, fetch)
  return <>{data.map(v => <div key={v.key}>{v.data ? v.data : v.error.toString()}</div>)}</>
}
  • Render props
const Demo = () => {
  const { items } = useSWRAggregator(keys, fetch, {
    keepPreviousData: true,
    children: (swrResult, index, swrAggregator) => {
      return <div>{swrResult.data ? swrResult.data : "loading"}</div>;
    }
  })
  return <>{items}</>
}
  • Toghter
const Demo = () => {
  const { data, items } = useSWRAggregator(keys, fetch, {
    keepPreviousData: true,
    children: (value, index, array) => {
      return <div>{value.data ? value.data : 'loading'}</div>
    }
  })
  return (
    <div>
      <div>{items}</div>
      <div>
        {data ? (
          data.map(v => (
            <div key={v.key}>{v.data ? v.data : v.error.toString()}</div>
          ))
        ) : (
          <div>loading</div>
        )}
      </div>
    </div>
  )
}

Problems

  • Item component will use useSWR internally, it wouldn't share config with useSWRAggregator and needs extra code to config useSWR
const Demo = () => {
  const { items } = useSWRAggregator(keys, fetch, {
    keepPreviousData: true,
    children: (value, index, array) => {
      return <div>{value.data ? value.data : 'loading'}</div>
    }
  })
  // Has to use SWRConfig config useSWR in items
  return <SWRConfig value={yourconfig}>{items}</SWRConfig>
}

config individually

const Demo = () => {
  const { items } = useSWRAggregator(keys, fetch, {
    keepPreviousData: true,
    children: (value, index, array) => {
      return <div>{value.data ? value.data : 'loading'}</div>
    }
  })
  // Has to use SWRConfig config useSWR in items
  return items.map(v => <SWRConfig value={yourconfig}>{items}</SWRConfig>)
}

Demo

https://codesandbox.io/s/useswraggregator-je1dxf

promer94 avatar Jun 09 '22 16:06 promer94

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit be97fb9d1e46011fe47614cabe7a48bc44f6f4a6:

Sandbox Source
SWR-Basic Configuration
SWR-States Configuration
SWR-Infinite Configuration
SWR-SSR Configuration
useSWRAggregator PR

codesandbox-ci[bot] avatar Jun 09 '22 16:06 codesandbox-ci[bot]

any plan on crossing the finish line? look forward to using this feature

0xbe1 avatar Dec 02 '22 08:12 0xbe1

Would also like to utilize this! We have a workaround that involves looping thru the list of service calls, creating a component for each which uses the useSwr() hook, then utilizes a callback/reducer to send the data back to the parent which aggregates everything.

Our particular use case is we have data that's loading in different components throughout the page, but then also have a component that needs to know the status of which panels have had their data loaded already.

Conmiro avatar Dec 14 '22 22:12 Conmiro