Can't define/update a Mutable

In the context of the Framework I am trying to make a component like so:

import {Mutable} from 'npm:@observablehq/stdlib';

const isLoggedIn = Mutable(false);
const setLoginStatus = (status) => isLoggedIn.value = status;

setLoginStatus(true);

The code causes an error when attempting to update the mutable variable’s value. It seems that while the variable is created by Mutable(), function change remains undefined.

Can somebody please explain what I’m doing wrong?

It’s hard to recommend a fix without seeing the rest of your code, but the immediate problem is that you’re setting the mutable’s value synchronously after creation rather than waiting until the Mutable gets initialized by the Observable runtime; that’s why change is not yet defined.

If you want the initial value to be true, then say:

const isLoggedIn = Mutable(true);

I suspect the bigger issue here is that you aren’t defining the Mutable as a top-level variable. Generally speaking, a Mutable should be declared in a page, not in a component. In Observable, all reactive state lives at the page level. You can pass state values (and mutators) into components when rendering, but components can’t define their own internal reactive state.

So, more commonly you’d declare the Mutable in your page like so:

```js
const isLoggedIn = Mutable(false);
const setLoggedIn = (value) => isLoggedIn.value = value;
```

Then you could have components that receive either isLoggedIn or setLoggedIn as props. For example, this one displays the current status:

import {html} from "npm:htl";

export function UserStatus({isLoggedIn}) {
  return html`You are currently ${isLoggedIn ? "logged in" : "logged out"}.`
}

And this one mutates it:

import {html} from "npm:htl";

export function UserActions({setLoggedIn}) {
  return html`<div>
    <button onclick=${() => setLoggedIn(true)}>Sign in</button>
    <button onclick=${() => setLoggedIn(false)}>Sign out</button>
  </div>`;
}

To use these on the page, say:

```js echo
import {UserActions} from "./UserActions.js";
import {UserStatus} from "./UserStatus.js";
```

${UserStatus({isLoggedIn})}

${UserActions({setLoggedIn})}

Live example:

https://mbostock.observablehq.cloud/framework-help/2024-04-13-mutable/

1 Like

Yes, the bigger issue was my actual problem. The fact that components can’t define their own reactive state was not clear to me. Thank you very much for the clarification and the example!

1 Like