> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/meteor/meteor/llms.txt
> Use this file to discover all available pages before exploring further.

# ReactiveVar

> Single reactive variable for use with Tracker

ReactiveVar provides a single reactive value that can be get and set, automatically invalidating computations that depend on it.

## Installation

```bash theme={null}
meteor add reactive-var
```

## Overview

ReactiveVar is:

* A container for a single reactive value
* Similar to Session variables but scoped (not global)
* More flexible than Session (can hold any value, not just EJSON)
* Integrated with Tracker for automatic reactivity
* Efficient at absorbing unnecessary invalidations

## Package Information

* **Version**: 1.0.13
* **Summary**: Reactive variable
* **Dependencies**: `tracker`
* **Export**: `ReactiveVar`

## Basic Usage

### Creating a ReactiveVar

```javascript theme={null}
import { ReactiveVar } from 'meteor/reactive-var';

// Create with initial value
const counter = new ReactiveVar(0);

const message = new ReactiveVar('Hello');

const user = new ReactiveVar(null);

const settings = new ReactiveVar({ theme: 'dark', lang: 'en' });
```

### Getting Values

```javascript theme={null}
// Get current value (reactive)
const value = counter.get();

console.log('Counter:', counter.get());
// 0
```

### Setting Values

```javascript theme={null}
// Set new value
counter.set(5);

message.set('Hello, World!');

user.set({ name: 'Alice', id: 123 });
```

## Reactive Updates

ReactiveVar automatically invalidates computations:

```javascript theme={null}
const count = new ReactiveVar(0);

// This computation depends on count
Tracker.autorun(() => {
  console.log('Count is:', count.get());
});
// Prints: Count is: 0

count.set(1);
// Prints: Count is: 1

count.set(2);
// Prints: Count is: 2
```

## Constructor

```javascript theme={null}
new ReactiveVar(initialValue, equalsFunc)
```

<ParamField path="initialValue" type="any" required>
  The initial value to set
</ParamField>

<ParamField path="equalsFunc" type="function">
  Optional function to determine if values are equal. Called with `(oldValue, newValue)`. If it returns true, no invalidation occurs.
</ParamField>

### Custom Equality Function

By default, ReactiveVar uses a conservative equality check:

```javascript theme={null}
// Default behavior: object changes always trigger updates
const obj = new ReactiveVar({ x: 1 });

Tracker.autorun(() => {
  console.log('Object:', obj.get());
});

obj.set({ x: 1 }); // Triggers update even though value is same

// Custom equality: deep comparison
const deepObj = new ReactiveVar({ x: 1 }, (a, b) => {
  return JSON.stringify(a) === JSON.stringify(b);
});

Tracker.autorun(() => {
  console.log('Deep object:', deepObj.get());
});

deepObj.set({ x: 1 }); // No update - values are equal
deepObj.set({ x: 2 }); // Updates - values differ
```

## Methods

### get()

Returns the current value and registers a dependency:

```javascript theme={null}
const value = myVar.get();
```

<Info>
  Calling `get()` inside a reactive computation (like `Tracker.autorun`) establishes a dependency.
</Info>

### set(newValue)

Sets a new value and invalidates dependents if changed:

```javascript theme={null}
myVar.set(newValue);
```

<ParamField path="newValue" type="any" required>
  The new value to set
</ParamField>

## Default Equality

The default equality function (`ReactiveVar._isEqual`):

```javascript theme={null}
// These are considered equal:
var1.set(42);
var1.set(42); // No invalidation

var2.set(true);
var2.set(true); // No invalidation

var3.set('hello');
var3.set('hello'); // No invalidation

var4.set(null);
var4.set(null); // No invalidation

// These always trigger invalidations:
var5.set({ x: 1 });
var5.set({ x: 1 }); // Invalidates! Objects are not deep-compared

var6.set([1, 2, 3]);
var6.set([1, 2, 3]); // Invalidates! Arrays are not deep-compared
```

The default check only considers primitive values equal:

* `number`
* `boolean`
* `string`
* `undefined`
* `null`

## Patterns and Examples

### Pattern: Component State

```javascript theme={null}
class Counter {
  constructor() {
    this.count = new ReactiveVar(0);
  }
  
  increment() {
    this.count.set(this.count.get() + 1);
  }
  
  decrement() {
    this.count.set(this.count.get() - 1);
  }
  
  reset() {
    this.count.set(0);
  }
}

const counter = new Counter();

Tracker.autorun(() => {
  console.log('Count:', counter.count.get());
});

counter.increment(); // Prints: Count: 1
counter.increment(); // Prints: Count: 2
counter.reset();     // Prints: Count: 0
```

### Pattern: Loading State

```javascript theme={null}
const isLoading = new ReactiveVar(false);

async function fetchData() {
  isLoading.set(true);
  
  try {
    const data = await fetch('/api/data');
    return data;
  } finally {
    isLoading.set(false);
  }
}

// In template helper or React component
Tracker.autorun(() => {
  if (isLoading.get()) {
    showLoadingSpinner();
  } else {
    hideLoadingSpinner();
  }
});
```

### Pattern: Form State

```javascript theme={null}
class FormState {
  constructor() {
    this.username = new ReactiveVar('');
    this.email = new ReactiveVar('');
    this.password = new ReactiveVar('');
  }
  
  isValid() {
    return (
      this.username.get().length >= 3 &&
      this.email.get().includes('@') &&
      this.password.get().length >= 8
    );
  }
  
  clear() {
    this.username.set('');
    this.email.set('');
    this.password.set('');
  }
  
  getData() {
    return {
      username: this.username.get(),
      email: this.email.get(),
      password: this.password.get()
    };
  }
}

const form = new FormState();

// Reactive validation
Tracker.autorun(() => {
  const valid = form.isValid();
  submitButton.disabled = !valid;
});
```

### Pattern: Derived ReactiveVars

```javascript theme={null}
const firstName = new ReactiveVar('John');
const lastName = new ReactiveVar('Doe');

// Computed value (not a ReactiveVar, but reactive)
function fullName() {
  return `${firstName.get()} ${lastName.get()}`;
}

Tracker.autorun(() => {
  console.log('Full name:', fullName());
});
// Prints: Full name: John Doe

firstName.set('Jane');
// Prints: Full name: Jane Doe

// Or create a ReactiveVar that depends on others
const fullNameVar = new ReactiveVar('');

Tracker.autorun(() => {
  fullNameVar.set(`${firstName.get()} ${lastName.get()}`);
});
```

### Pattern: Toggle State

```javascript theme={null}
class Toggle {
  constructor(initialState = false) {
    this._state = new ReactiveVar(initialState);
  }
  
  get() {
    return this._state.get();
  }
  
  set(value) {
    this._state.set(!!value);
  }
  
  toggle() {
    this._state.set(!this._state.get());
  }
  
  on() {
    this._state.set(true);
  }
  
  off() {
    this._state.set(false);
  }
}

const darkMode = new Toggle(false);

Tracker.autorun(() => {
  document.body.className = darkMode.get() ? 'dark' : 'light';
});

darkMode.toggle(); // Switches to dark mode
```

### Pattern: Array Modifications

Since arrays are objects, you need to handle them specially:

```javascript theme={null}
// This doesn't work as expected:
const items = new ReactiveVar([]);
items.get().push('new item'); // No invalidation!

// Correct approach:
function addItem(item) {
  const current = items.get();
  items.set([...current, item]); // Creates new array
}

function removeItem(index) {
  const current = items.get();
  items.set(current.filter((_, i) => i !== index));
}

// Or use custom equality:
const items = new ReactiveVar([], (a, b) => {
  // Always consider different (update on every set)
  return false;
});
```

## Comparison with Session

### ReactiveVar

```javascript theme={null}
const myVar = new ReactiveVar(0);
myVar.set(5);
const value = myVar.get();

// ✓ Scoped (not global)
// ✓ Can hold any value (functions, DOM nodes, etc.)
// ✓ Must be imported and passed around
// ✓ Not persisted across hot code push
// ✓ Custom equality functions
```

### Session

```javascript theme={null}
Session.set('myVar', 0);
Session.set('myVar', 5);
const value = Session.get('myVar');

// ✓ Global (accessible anywhere)
// ✓ Only EJSON-able values
// ✓ Available everywhere by default
// ✓ Persisted across hot code push
// ✓ String keys can collide
```

## Using with React

```javascript theme={null}
import { ReactiveVar } from 'meteor/reactive-var';
import { useTracker } from 'meteor/react-meteor-data';

const counter = new ReactiveVar(0);

function Counter() {
  const count = useTracker(() => counter.get(), []);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => counter.set(count + 1)}>
        Increment
      </button>
    </div>
  );
}
```

## Using with Blaze

```javascript theme={null}
// In template
Template.myTemplate.onCreated(function() {
  this.counter = new ReactiveVar(0);
});

Template.myTemplate.helpers({
  count() {
    return Template.instance().counter.get();
  }
});

Template.myTemplate.events({
  'click .increment'(event, instance) {
    const current = instance.counter.get();
    instance.counter.set(current + 1);
  }
});
```

```handlebars theme={null}
<template name="myTemplate">
  <p>Count: {{count}}</p>
  <button class="increment">Increment</button>
</template>
```

## Performance Considerations

<Tip>
  **ReactiveVar is lightweight** - Very little overhead compared to Session
</Tip>

<Tip>
  **Absorbs redundant updates** - Setting the same primitive value multiple times doesn't cause reruns
</Tip>

<Warning>
  **Object/Array updates** - Always trigger invalidations unless using custom equality
</Warning>

```javascript theme={null}
// Efficient for primitives
const count = new ReactiveVar(0);
count.set(0); // No invalidation
count.set(0); // No invalidation
count.set(1); // Invalidates

// Inefficient for objects without custom equality
const obj = new ReactiveVar({ x: 1 });
obj.set({ x: 1 }); // Invalidates
obj.set({ x: 1 }); // Invalidates again!

// Better: use custom equality or immutable updates
const obj2 = new ReactiveVar(
  { x: 1 },
  (a, b) => a.x === b.x
);
obj2.set({ x: 1 }); // No invalidation
obj2.set({ x: 2 }); // Invalidates
```

## Debugging

```javascript theme={null}
const myVar = new ReactiveVar(0);

// Track all changes
const originalSet = myVar.set.bind(myVar);
myVar.set = function(newValue) {
  console.log('Setting value:', newValue);
  console.trace('Called from:');
  return originalSet(newValue);
};

// Count dependents (internal API)
console.log('Dependents:', myVar._numListeners());
```

## Related Packages

<CardGroup cols={2}>
  <Card title="reactive-dict" icon="book" href="/packages/reactive-dict">
    Reactive key-value dictionaries
  </Card>

  <Card title="tracker" icon="rotate" href="/packages/tracker">
    Reactive computation system
  </Card>

  <Card title="session" icon="memory" href="#">
    Global reactive state
  </Card>
</CardGroup>

## Source Code

* [packages/reactive-var](https://github.com/meteor/meteor/tree/devel/packages/reactive-var)
