Repository: zeit/swr
Pull request #106 (2019-11-09 18:42:16)

Adding initialData
Can partially help in resolving the issue: #11 in the interim 

(worded weirdly to avoid a auto-close)

quietshu: BTW, can you add this new option to the readme? 

Patch:
diff --git a/README.md b/README.md
index f2ef10c..28a0c91 100644
--- a/README.md
+++ b/README.md
@@ -114,6 +114,7 @@ const { data, error, isValidating, revalidate } = useSWR(key, fetcher, options)
 - `onSuccess`: callback function when a request finishs successfully
 - `onError`: callback function when a request returns an error
 - `onErrorRetry`: handler for [error retry](#error-retries)
+- `initialData`: initial data to be returned (note: This is per-hook)
 
 When under a slow network (2G, <= 70Kbps), `errorRetryInterval` will be 10s, and
 `loadingTimeout` will be 5s by default.
diff --git a/src/types.ts b/src/types.ts
index 65b92c9..00f46da 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,4 +1,9 @@
-export interface ConfigInterface<Data = any, Error = any> {
+export type fetcherFn<Data> = (...args: any) => Data | Promise<Data>
+export interface ConfigInterface<
+  Data = any,
+  Error = any,
+  Fn extends fetcherFn<Data> = fetcherFn<Data>
+> {
   errorRetryInterval?: number
   loadingTimeout?: number
   focusThrottleInterval?: number
@@ -8,8 +13,9 @@ export interface ConfigInterface<Data = any, Error = any> {
   refreshWhenHidden?: boolean
   revalidateOnFocus?: boolean
   shouldRetryOnError?: boolean
-  fetcher?: any
+  fetcher?: Fn
   suspense?: boolean
+  initialData?: Data
 
   onLoadingSlow?: (key: string, config: ConfigInterface<Data, Error>) => void
   onSuccess?: (
diff --git a/src/use-swr-pages.tsx b/src/use-swr-pages.tsx
index c22b6ba..d2babef 100644
--- a/src/use-swr-pages.tsx
+++ b/src/use-swr-pages.tsx
@@ -88,7 +88,7 @@ function App () {
 }
 */
 
-export function useSWRPages<OffsetType, Data, Error>(
+export function useSWRPages<OffsetType = any, Data = any, Error = any>(
   pageKey: string,
   pageFn: pageComponentType<OffsetType, Data, Error>,
   SWRToOffset: pageOffsetMapperType<OffsetType, Data, Error>,
diff --git a/src/use-swr.ts b/src/use-swr.ts
index 8fbd496..ca60f32 100644
--- a/src/use-swr.ts
+++ b/src/use-swr.ts
@@ -18,7 +18,8 @@ import {
   triggerInterface,
   mutateInterface,
   broadcastStateInterface,
-  responseInterface
+  responseInterface,
+  fetcherFn
 } from './types'
 
 import defaultConfig, {
@@ -111,14 +112,14 @@ function useSWR<Data = any, Error = any>(
 ): responseInterface<Data, Error>
 function useSWR<Data = any, Error = any>(
   key: keyInterface,
-  fn?: Function,
+  fn?: fetcherFn<Data>,
   config?: ConfigInterface<Data, Error>
 ): responseInterface<Data, Error>
 function useSWR<Data = any, Error = any>(
   ...args
 ): responseInterface<Data, Error> {
   let _key: keyInterface,
-    fn: Function | undefined,
+    fn: fetcherFn<Data> | undefined,
     config: ConfigInterface<Data, Error> = {}
   if (args.length >= 1) {
     _key = args[0]
@@ -157,7 +158,7 @@ function useSWR<Data = any, Error = any>(
   const shouldReadCache = config.suspense || !useHydration()
 
   // stale: get from cache
-  let [data, setData] = useState(shouldReadCache ? cacheGet(key) : undefined)
+  let [data, setData] = useState((shouldReadCache ? cacheGet(key) : undefined) || config.initialData)
   let [error, setError] = useState(
     shouldReadCache ? cacheGet(keyErr) : undefined
   )
@@ -322,7 +323,7 @@ function useSWR<Data = any, Error = any>(
     // and trigger a revalidation
 
     const currentHookData = dataRef.current
-    const latestKeyedData = cacheGet(key)
+    const latestKeyedData = cacheGet(key) || config.initialData
 
     // update the state if the key changed or cache updated
     if (


Test patch:
diff --git a/test/use-swr.test.tsx b/test/use-swr.test.tsx
index fcc1566..65639d2 100644
--- a/test/use-swr.test.tsx
+++ b/test/use-swr.test.tsx
@@ -1,13 +1,7 @@
-import React, { Suspense, ReactNode, useState, useEffect } from 'react'
-import {
-  cleanup,
-  render,
-  waitForDomChange,
-  act,
-  fireEvent
-} from '@testing-library/react'
+import { act, cleanup, fireEvent, render, waitForDomChange } from '@testing-library/react'
+import React, { ReactNode, Suspense, useEffect, useState } from 'react'
 
-import useSWR, { trigger, mutate, SWRConfig } from '../src'
+import useSWR, { mutate, SWRConfig, trigger } from '../src'
 
 class ErrorBoundary extends React.Component<{ fallback: ReactNode }> {
   state = { hasError: false }
@@ -188,39 +182,55 @@ describe('useSWR', () => {
     await act(() => new Promise(res => setTimeout(res, 100)))
     expect(container.textContent).toMatchInlineSnapshot(`"err err err"`)
   })
-
+  
   it('should accept object args', async () => {
     const obj = { v: 'hello' }
     const arr = ['world']
-
+    
     function Page() {
       const { data: v1 } = useSWR(
         ['args-1', obj, arr],
         (a, b, c) => a + b.v + c[0]
       )
-
+      
       // reuse the cache
       const { data: v2 } = useSWR(['args-1', obj, arr], () => 'not called!')
-
+      
       // different object
       const { data: v3 } = useSWR(
         ['args-2', obj, 'world'],
         (a, b, c) => a + b.v + c
       )
-
+      
       return (
         <div>
           {v1}, {v2}, {v3}
         </div>
       )
     }
-    const { container } = render(<Page />)
-
+    
+    const { container } = render(<Page/>)
+    
     await waitForDomChange({ container })
     expect(container.textContent).toMatchInlineSnapshot(
       `"args-1helloworld, args-1helloworld, args-2helloworld"`
     )
   })
+  
+  it('should accept initial data', async () => {
+    function Page() {
+      const { data } = useSWR('initial-data-1', () => 'SWR', { initialData: 'Initial' })
+      return <div>hello, {data}</div>
+    }
+  
+    const { container } = render(<Page/>)
+  
+    expect(container.firstChild.textContent).toMatchInlineSnapshot(`"hello, Initial"`)
+    await waitForDomChange({ container }) // mount
+    expect(container.firstChild.textContent).toMatchInlineSnapshot(
+      `"hello, SWR"`
+    )
+  })
 })
 
 describe('useSWR - refresh', () => {
@@ -625,14 +635,14 @@ describe('useSWR - suspense', () => {
 
   it('should render multiple SWR fallbacks', async () => {
     function Section() {
-      const { data: v1 } = useSWR(
+      const { data: v1 } = useSWR<number>(
         'suspense-2',
         () => new Promise(res => setTimeout(() => res(1), 100)),
         {
           suspense: true
         }
       )
-      const { data: v2 } = useSWR(
+      const { data: v2 } = useSWR<number>(
         'suspense-3',
         () => new Promise(res => setTimeout(() => res(2), 100)),
         {
@@ -672,7 +682,7 @@ describe('useSWR - suspense', () => {
     expect(container.textContent).toMatchInlineSnapshot(`"hello"`)
   })
 
-  it.only('should throw errors', async () => {
+  it('should throw errors', async () => {
     function Section() {
       const { data } = useSWR(
         'suspense-5',