I know that sometimes when the frontend receives data from mongoose, it is as a read-only document. However, when I go the error today: TypeError: "contracts" is read-only, my first thought was this and so I assigned a new variable to the data, tried running a map on the data (data.map(x => x)), tried assigning the data with a spread operator, but I keep getting the same error, so I really dont know what I’m missing.
Heres the data that comes in, as logged in the console:
Array [ {…}, {…} ]
​
0: Object { _id: "6205a8313fe12d6b4ec354c4", fullName: "Johnny Cochrane", contracts: {…} }
​​
_id: "6205a8313fe12d6b4ec354c4"
​​
contracts: Object { type: "Quarterly", hours: 120 }
​​
fullName: "Johnny Cochrane"
​​
<prototype>: Object { … }
​
1: Object { _id: "6217c1b73fe12d6b4ec3550e", fullName: "Sheila Thompson", contracts: {…} }
And here is where I’m assigning new fields thats throwing the error:
useEffect(() => {
if(data) {
let dataArr = [...data]
const contractsByMonth = (type, hours) => {
if (type === "Quarterly") {
let monthHours = hours / 3
return +(Math.round(monthHours + "e+2") + "e-2");
}
}
dataArr.map(i => {
let monthly = contractsByMonth(i.contracts.type, i.contracts.hours)
i.contracts = {type: i.contracts.type, monthHours: monthly, total: i.contracts.hours }
...
}, [data])
So I know the data is there, it is not in a string that needs to be parsed, and even trying to reassign it is not seeming to work. Appreciate any thoughts anyone has. Shouldnt be the issue, but I’m using RTK Query to get the data, but I use it all the time without this issue, my guess is that its treating the data as the original mongoose returned document.
EDIT
Adding more of the components code:
const HoursGraph = () => {
const {id} = useAuth()
const userId = id
...
const [pullData, {data}] = useGetSnapshotMutation({userId, isStart, isEnd})
// RTK Query mutation which pulls the data- its an aggregation and I pass criteria through the body, but I've done this plenty without issue
...
useEffect(() => {
...
if (index === 0) {
start = new Date(end).setDate(1)
} else if (index === 1) {
const startMth = currentMth - 1
console.log(currentMth, startMth)
start = new Date(new Date(new Date(end).setMonth(startMth, 1)).setHours(0, 0, 0)).toISOString()
} else if (index === 2) {
const startMth = currentMth -2
console.log(currentMth, startMth)
start = new Date(new Date(new Date(end).setMonth(startMth, 1)).setHours(0, 0, 0)).toISOString()
}
setIsStart(start)
setIsEnd(end)
}, [])
useEffect(() => {
if(isStart && isEnd && userId) {
pullData({userId, isStart, isEnd})
}
}, [isStart, isEnd, userId])
console.log("snapshot data: ", data) //this logs the data referenced above
useEffect(() => {
if(data) {
let dataArr = [...data]
const contractsByMonth = (type, hours) => {
if (type === "Quarterly") {
let monthHours = hours / 3
return +(Math.round(monthHours + "e+2") + "e-2");
}
}
dataArr.map(i => {
let monthly = contractsByMonth(i.contracts.type, i.contracts.hours)
i.contracts = {type: i.contracts.type, monthHours: monthly, total: i.contracts.hours } //This throws the error
let colors
let sum
let qtrSum = i.hours.reduce((acc, curr) => acc.count + curr.count)
console.log("sum: ", qtrSum)
const color= ['#73E078', '#26A69A', '#26C6DA', '#29B6F6']
i.hours = i.hours.map(x => {return ( {date: x.date, count: x.count, month: monthly } )})
i[colors] = color
i[sum] = qtrSum
})
console.log(data)
...
}, [data])
>Solution :
Instead of assigning to i.contracts, which appears to be read-only, try
dataArr.map((i) => {
const monthly = /*yourt code here*/
const contracts = {...i.contracts, monthHours: monthly, total: i.contracts.hours} //this obviously has a different type than the original i.contracts, which is an issue you will still have to deal with
return {...i, contracts};
})
This issue is probably that the contracts you’re trying to mutate on i is typed as read-only (and you’re trying to mutate it to a different type). RTK Query uses immer under the hood, which might protect you from the consequences of rewriting that property if you make the edit in the transformResponse field of your endpoint. It might not let you change the type in the process though.