Recoil: Uncaught TypeError: Cannot freeze

Created on 26 Jun 2020  路  6Comments  路  Source: facebookexperimental/Recoil

When using the setter function to update a value of an atom you cannot pass any object as Object.freeze(value) can throw an error in some cases.

In my case I am passing a Firebase user to update the value of an atom but I am having Uncaught TypeError: Cannot freeze error. The error happens here: https://github.com/facebookexperimental/Recoil/blob/e018c3abfb68f559bf493ba244079fa73f89e79b/src/util/Recoil_deepFreezeValue.js#L65

Example

const userState = atom({
  key: 'userState',
  default: null
});

const [user, setUser] = useRecoilState(userState);

auth.onAuthStateChanged(fetchedUser => {
    setUser(fetchedUser);
});

Another example would be that passing something like location.ancestorOrigins will also throw the same error.

What would be the solution? Should I just take some of the properties and create a new simpler object and pass it to the setter function or somehow convert the entire user object into a freezable object?

Most helpful comment

Recoil freezes stored values for atoms in part to help ensure that all state changes are visible and propagated as updates through the data-flow graph. If stored values are mutated in an attempt to change state it won't be tracked or update dependencies. To help catch this, we deep freeze objects in development. However, there are situations where mutable objects may need to be used as values that don't logically represent Recoil state changes. You can use the dangerouslyAllowMutability option when constructing your atom to allow for this.

All 6 comments

Since recoil is experimental, you would have more stable results by only using primitives. This is also a good practice for recoil's state persistence.

Having the same issue

Apart from not using objects, is there a way to safely use objects, similar to how an array is used in the Documentation

```
const todoListState = atom({
key: 'todoListState',
default: [],
});

function TodoItemCreator() {
const [inputValue, setInputValue] = useState('');
const setTodoList = useSetRecoilState(todoListState);

const addItem = () => {
setTodoList((oldTodoList) => [
...oldTodoList,
{
id: getId(),
text: inputValue,
isComplete: false,
},
]);
setInputValue('');
};

const onChange = ({target: {value}}) => {
setInputValue(value);
};

return (




);
}

// utility for creating unique Id
let id = 0;
function getId() {
return id++;
}

You can use objects, but you can expect lesser errors when using primitive ones.

Recoil freezes stored values for atoms in part to help ensure that all state changes are visible and propagated as updates through the data-flow graph. If stored values are mutated in an attempt to change state it won't be tracked or update dependencies. To help catch this, we deep freeze objects in development. However, there are situations where mutable objects may need to be used as values that don't logically represent Recoil state changes. You can use the dangerouslyAllowMutability option when constructing your atom to allow for this.

Thank you @cybervaldez and @drarmstr for the explanation!, I'll be trying the dangerouslyAllowMutability option.

Was this page helpful?
0 / 5 - 0 ratings