Skip to content

Bug - Tanstack Plugin V5 - Optimistic updates incorrectly modify unrelated entities within the cache. #1890

@roomman

Description

@roomman

Description and expected behavior
When performing an optimistic update to create a new record for a specific model, the new record is erroneously added to another model’s data in the cache.

Additional context
The following test should be added to packages/plugins/tanstack-query/tests/react-hooks-v5.test.tsx on the dev branch in order to replicate the issue:

    it('should apply optimistic updates to user1 without affecting user2', async () => {
        const { queryClient, wrapper } = createWrapper();

        // populate the cache with two users each with a post
        const userData: any[] = [
            { id: '1', name: 'user1', posts: [{ id: '1', title: 'post1' }] },
            { id: '2', name: 'user2', posts: [{ id: '2', title: 'post2' }] },
        ];

        nock(BASE_URL)
            .get('/api/model/User/findMany')
            .query(true)
            .reply(200, () => {
                console.log('Querying data:', JSON.stringify(userData));
                return { data: userData };
            })
            .persist();

        const { result: userResult } = renderHook(
            () =>
                useModelQuery(
                    'User',
                    makeUrl('User', 'findMany'),
                    { include: { posts: true } },
                    { optimisticUpdate: true }
                ),
            {
                wrapper,
            }
        );

        await waitFor(() => {
            expect(userResult.current.data).toHaveLength(2);
        });

        // mock a post request to create a new post for user1
        nock(BASE_URL)
            .post('/api/model/Post/create')
            .reply(200, () => {
                console.log('Not mutating data');
                return { data: null };
            });

        const { result: mutationResult } = renderHook(
            () =>
                useModelMutation('Post', 'POST', makeUrl('Post', 'create'), modelMeta, {
                    optimisticUpdate: true,
                    invalidateQueries: false,
                }),
            {
                wrapper,
            }
        );

        act(() =>
            mutationResult.current.mutate({
                data: { title: 'post3', owner: { connect: { id: '1' } } },
            })
        );

        // assert that the post was created and connected to user1, and user2 was unaffected
        await waitFor(() => {
            const cacheData: any = queryClient.getQueryData(
                getQueryKey(
                    'User',
                    'findMany',
                    { include: { posts: true } },
                    { infinite: false, optimisticUpdate: true }
                )
            );
            const user1 = cacheData.find((user: any) => user.id === '1');
            expect(user1.posts).toHaveLength(2);
            expect(user1.posts.find((post: any) => post.title === 'post3')).toMatchObject({
                $optimistic: true,
                id: expect.any(String),
                title: 'post3',
                ownerId: '1',
            });

            const user2 = cacheData.find((user: any) => user.id === '2');
            expect(user2.posts).toHaveLength(1);
            expect(user2.posts.find((post: any) => post.title === 'post3')).toBeUndefined();
        });
    });

The test fails because 'post3' appears in the cached data for both users, not just user1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions