morrys / react-relay-offline

TypeScript library files for Relay Modern Offline
https://morrys.github.io/react-relay-offline/docs/react-relay-offline.html
MIT License
227 stars 15 forks source link

isLoading seemingly incorrectly returning true for a single render #138

Closed matt-dalton closed 1 year ago

matt-dalton commented 1 year ago

I have a slightly confusing situation that's causing a problem in a test, but possibly might happen outside of tests also.

My code looks like this (using v5.0.0 of react-relay-offline):

        const {
            data,
            error: _error,
            retry,
            isLoading: relayLoading,
        } = useQuery(options.query, variables, options)

        const isConnected = useIsConnected()
        const isOffline = !isConnected

        if (!isRehydrated) {
            return null
        }

        let networkError = false
        let error = _error
        if (!data && isOffline) {
            // We're offline and have an empty cache: show the network error screen.
            error = new NetworkError()
            networkError = true
        }

        const mostRecent = relayEnv.environment.mock.getAllOperations()[0] //debugging

        console.log(
            `data returned: ${!!data}, `,
            `relayLoading: ${relayLoading}, `,
            `isConnected: ${isConnected}, `,
            `relayOperations: ${relayEnv.environment.mock
                .getAllOperations()
                .map((operation) => operation && operation.request.node.fragment.name)}, `,
            `environment.mock.isLoading: ${
                mostRecent && relayEnv.environment.mock.isLoading(mostRecent)
            }`
        )

        return (
            <Component />

I run one test fine, switching from one test to the other I run:

beforeEach(() => {
        environment.clearCache()
        jest.clearAllMocks()
        jest.useFakeTimers()
    })

    afterEach(async () => {
        jest.runOnlyPendingTimers()
        jest.useRealTimers()
    })

then the second test I get this sequence of logs:

data returned: false,  relayLoading: true,  isConnected: undefined,  relayOperations: HomeScreenQuery,  environment.mock.isLoading: false

data returned: false,  relayLoading: true,  isConnected: true,  relayOperations: HomeScreenQuery,  environment.mock.isLoading: false

data returned: false,  relayLoading: false,  isConnected: true,  relayOperations: HomeScreenQuery,  environment.mock.isLoading: false // <<< This is the problematic render

data returned: false,  relayLoading: true,  isConnected: undefined,  relayOperations: HomeScreenQuery,  environment.mock.isLoading: false

data returned: false,  relayLoading: true,  isConnected: true,  relayOperations: HomeScreenQuery,  environment.mock.isLoading: false

// At this point I run "environment.mock.resolveMostRecentOperation"

data returned: true,  relayLoading: false,  isConnected: true,  relayOperations: ,  environment.mock.isLoading: undefined // <<< At this point we're OK

If I switch the tests round I get the same thing on the second test. So I think there must be some state persisting between the stores (or a mock not being rest somehow)

I appreciate I haven't shown you any of the code around this (I didn't want to overwhelm with information just yet), but are you aware of a situation that could occur where isLoading changes from true -> false even though no data is returned yet, before then loading a couple of cycles later? It looks to me like there's an operation logged in Relay the entire time as well.

Is there anything I could log from the store (or elsewhere) that might help debug this?

I have seen the odd random error screen flash in our app, which makes me wonder if this also happens outside of tests.

matt-dalton commented 1 year ago

Ahh...after some digging around in the source code I realised this was because I was using environment.clearCache instead of environment.mockClear in my test's beforeEach.

In case someone runs into anything similar, to solve some similar issues I also had to properly reset my environment between each test using createMockEnvironment.