TanStack / query

🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.
https://tanstack.com/query
MIT License
40.09k stars 2.69k forks source link

TypeError: Cannot assign to read only property '0' of object '[object Array]' #7361

Closed igorSurz closed 2 weeks ago

igorSurz commented 2 weeks ago

Describe the bug

Please take a quick look on this code, I am struggling with this error, you can see my code for mutations that makes this query to refetch all records. And replace specific record I cant because of an error

queryClient.setQueryData(['boardData', currentBoardId, filterState], old => {
if (!old) return null;
const stateCopy = { ...old };

const foundIndex = stateCopy.records.findIndex(record => record.id === recordId);
const record = { ...stateCopy.records[foundIndex] };
if (foundIndex !== -1) {
    record.lastModified = Date.now();
    record.lastUserModified = data?.id;

    if (isNameEndpoint) {
        record.name = newValue;
    } else {
        const fieldToUpdate = fieldData.fieldId || fieldData.field;
        record[fieldToUpdate] = {
            value: newValue,
            meta: newMeta || null,
            brfId: fieldData?.id
        };
    }
       // stateCopy.records[foundIndex] = record <<<<--- Error

    const newRecords = [...stateCopy.records];
    newRecords[foundIndex] = record;
    stateCopy.records = newRecords;
}

return stateCopy;

Your minimal, reproducible example

...

Steps to reproduce

check the code snippet

Expected behavior

I thought that I was doing something like Object.freeze on my server side, but it is defiantly react query that freezes this array

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

Chrome, React

Tanstack Query adapter

react-query

TanStack Query version

v5

TypeScript version

no

Additional context

No response

TkDodo commented 2 weeks ago

you're definitely not making immutable updates as the docs say that we should. you're making shallow copies, but then deeply assign values with code like timer.meta.recordName = newValue

query does not freeze anything. we use structural sharing to keep as many references as possible to avoid unnecessary re-renders and re-computations. If you mutate state directly

igorSurz commented 2 weeks ago

@TkDodo hello there, thank you for your answer and for your time, I did as immutable as possible, may I ask you to check if this is the right approach and I got you?

upd: after these changes, I am still getting rerender of all records.

    queryClient.setQueryData(['boardData', currentBoardId, filterState], old => {
        if (!old) return null;
        const stateCopy = { ...old };

        const foundIndex = stateCopy.records.findIndex(record => record.id === recordId);
        let record = { ...stateCopy.records[foundIndex] };
        if (foundIndex !== -1) {
            record = {
                ...record,
                lastModified: Date.now(),
                lastUserModified: data?.id
            };

            if (isNameEndpoint) {
                record = {
                    ...record,
                    name: newValue
                };

            } else {
                const fieldToUpdate = fieldData.fieldId || fieldData.field;
                record[fieldToUpdate] = {
                    ...record[fieldToUpdate],
                    value: newValue,
                    meta: newMeta || null,
                    brfId: fieldData?.id
                };
            }

            const newRecords = [...stateCopy.records];
            newRecords[foundIndex] = record;
            stateCopy.records = newRecords;
        }