Skip to content

Best Practices ​

Learn the best practices for using Nexus State effectively in your applications.

General Guidelines ​

1. Use Descriptive Atom Names ​

Always give your atoms descriptive names for better debugging experience:

typescript
// Bad
const a = atom(0);

// Good
const countAtom = atom(0, 'counter');
const userAtom = atom({ name: '' }, 'user-profile');

2. Keep Atoms Small and Focused ​

Break down complex state into multiple small atoms rather than using large objects:

typescript
// Bad
const complexState = atom({
  user: { name: '', email: '' },
  settings: { theme: 'light', language: 'en' },
  preferences: { notifications: true }
});

// Good
const userAtom = atom({ name: '', email: '' }, 'user');
const settingsAtom = atom({ theme: 'light', language: 'en' }, 'settings');
const preferencesAtom = atom({ notifications: true }, 'preferences');

3. Use Computed Atoms for Derived State ​

Instead of recalculating derived values, use computed atoms:

typescript
// Bad
const total = computed(() => items.reduce((sum, item) => sum + item.price, 0));

// Good
const totalAtom = atom((get) => {
  const items = get(itemsAtom);
  return items.reduce((sum, item) => sum + item.price, 0);
}, 'total');

Framework-Specific Best Practices ​

React ​

1. Use Hooks Properly ​

typescript
// Bad - Unnecessary re-renders
function Counter() {
  const count = useAtom(countAtom);
  return <div>{count}</div>;
}

// Good - Extract only needed values
function Counter() {
  const [count, setCount] = useAtom(countAtom);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

2. Use Selector Pattern for Performance ​

typescript
// Bad - Component re-renders on any state change
function CounterDisplay() {
  const state = useAtom(stateAtom);
  return <div>{state.count}</div>;
}

// Good - Only re-renders when count changes
function CounterDisplay() {
  const count = useAtomState(stateAtom, (state) => state.count);
  return <div>{count}</div>;
}

Vue ​

1. Use Composition API ​

typescript
// Bad - Options API
export default {
  setup() {
    const count = useAtom(countAtom);
    return { count };
  }
};

// Good - Composition API with reactive
import { computed } from 'vue';

export default {
  setup() {
    const count = useAtom(countAtom);
    const doubled = computed(() => count.value * 2);
    return { count, doubled };
  }
};

Svelte ​

1. Use Svelte's Reactive Declarations ​

typescript
// Good - Leverage Svelte's reactivity
<script>
  import { useAtom } from '@nexus-state/svelte';
  import { countAtom } from './store';

  let count = useAtom(countAtom);
  $: doubled = count * 2;
</script>

<p>Count: {count}</p>
<p>Doubled: {doubled}</p>

Time Travel Best Practices ​

1. Use Descriptive Snapshot Names ​

typescript
// Good
store.captureSnapshot('Form Submission');
store.captureSnapshot('User Login');
store.captureSnapshot('Data Fetched');

2. Limit History Size ​

typescript
const store = createEnhancedStore([], {
  enableTimeTravel: true,
  maxHistory: 50 // Adjust based on your needs
});

3. Use Manual Snapshots for Important Actions ​

typescript
// Bad - Every action creates a snapshot
const store = createEnhancedStore([], { autoCapture: true });

// Good - Control snapshots for meaningful history points
const store = createEnhancedStore([], {
  enableTimeTravel: true,
  autoCapture: false
});

store.set(countAtom, 5);
store.captureSnapshot('User Adjusted Count');

Performance Optimization ​

1. Batch Updates ​

typescript
// Bad - Multiple updates cause multiple re-renders
store.set(countAtom, 1);
store.set(nameAtom, 'John');
store.set(emailAtom, 'john@example.com');

// Good - Batch updates
store.batch(() => {
  store.set(countAtom, 1);
  store.set(nameAtom, 'John');
  store.set(emailAtom, 'john@example.com');
});

2. Use Selectors in Framework Integrations ​

typescript
// React example
const count = useAtomState(countAtom, (state) => state.count);

3. Avoid Unnecessary Computations ​

typescript
// Bad - Expensive computation in computed atom
const expensiveAtom = atom((get) => {
  const data = get(dataAtom);
  return expensiveFunction(data); // Runs on every access
});

// Good - Use derived state with caching
const derivedAtom = atom((get) => {
  const data = get(dataAtom);
  return processDerivedData(data); // Cached result
});

Testing Best Practices ​

1. Test Atoms in Isolation ​

typescript
// Example.test.ts
import { atom } from '@nexus-state/core';
import { createEnhancedStore } from '@nexus-state/core';

describe('Counter Atom', () => {
  it('should increment correctly', () => {
    const countAtom = atom(0, 'count');
    const store = createEnhancedStore();

    expect(store.get(countAtom)).toBe(0);
    store.set(countAtom, 1);
    expect(store.get(countAtom)).toBe(1);
  });
});

2. Test Time Travel Functionality ​

typescript
it('should support undo/redo', () => {
  const countAtom = atom(0, 'count');
  const store = createEnhancedStore([], { enableTimeTravel: true });

  store.set(countAtom, 1);
  store.set(countAtom, 2);

  expect(store.get(countAtom)).toBe(2);

  store.undo();
  expect(store.get(countAtom)).toBe(1);

  store.redo();
  expect(store.get(countAtom)).toBe(2);
});

Common Pitfalls ​

1. Overusing Global State ​

Don't put everything in global state. Consider local state for UI-only data.

2. Large Atoms ​

Avoid large, complex atoms. Break them down into smaller, focused atoms.

3. Ignoring Performance ​

Use selectors, batching, and computed atoms wisely to maintain performance.

4. Not Using Names ​

Always name your atoms for better debugging experience.

Next Steps ​