DEV Community

Maxim Logunov
Maxim Logunov

Posted on

Using MobX Local Observable State in Observer Components

Introduction

MobX is a powerful state management library that makes state synchronization efficient and straightforward. While most MobX tutorials focus on global state management, local observable state in observer components is an equally important pattern that can simplify your React components while maintaining reactivity.

What is Local Observable State?

Local observable state refers to observable state that is scoped to a single component instance rather than being shared globally. This is created using MobX's makeObservable, makeAutoObservable, or observable directly within a component.

import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react-lite';

const Counter = observer(() => {
  const state = makeAutoObservable({
    count: 0,
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    },
    get double() {
      return this.count * 2;
    }
  });

  return (
    <div>
      <button onClick={state.decrement}>-</button>
      <span>{state.count}</span>
      <button onClick={state.increment}>+</button>
      <div>Double: {state.double}</div>
    </div>
  );
});
Enter fullscreen mode Exit fullscreen mode

When to Use Local Observable State

1. Component-Specific State

When you have state that's only relevant to a single component or its children, local observable state keeps your logic co-located and encapsulated.

2. Complex Local State

For components with:

  • Multiple interdependent state values
  • Derived values (computed properties)
  • Actions that modify multiple state values

3. Form Management

Forms with many fields, validation, and complex interaction patterns benefit greatly from local observable state:

const FormComponent = observer(() => {
  const form = makeAutoObservable({
    values: { name: '', email: '' },
    errors: {},
    get isValid() {
      return !Object.keys(this.errors).length;
    },
    setField(name, value) {
      this.values[name] = value;
      this.validateField(name);
    },
    validateField(name) {
      // validation logic
    },
    submit() {
      // submission logic
    }
  });

  return (
    <form onSubmit={form.submit}>
      <input
        value={form.values.name}
        onChange={(e) => form.setField('name', e.target.value)}
      />
      {/* other fields */}
    </form>
  );
});
Enter fullscreen mode Exit fullscreen mode

4. Avoiding Prop Drilling

When you need to share state between a parent and deeply nested children, but the state isn't needed globally.

5. Performance Optimization

Local observables can help minimize re-renders since only the observing component reacts to changes.

Why Use Local Observable State?

1. Encapsulation

Keeps state and logic close to where it's used, making components more self-contained and easier to reason about.

2. Reactive Benefits

You get all of MobX's reactivity (computed values, automatic tracking, efficient updates) without global state overhead.

3. Cleaner Code

Eliminates the need for useState and useMemo hooks for derived values - MobX handles this automatically.

4. Testability

Local state is easier to test in isolation since it's not tied to a global store.

Best Practices

  1. Use makeAutoObservable for most cases - it's concise and automatically infers everything.

  2. Initialize properly - Create the observable in the component body but outside any hooks or callbacks.

  3. Combine with hooks when needed:

const Component = observer(() => {
  const [externalData] = useState(/*...*/);
  const localState = useMemo(() => makeAutoObservable({ /*...*/ }), [externalData]);

  // ...
});
Enter fullscreen mode Exit fullscreen mode
  1. For class components, use makeObservable in the constructor.

  2. Consider disposal for any reactions or side effects:

useEffect(() => {
  const disposer = autorun(() => {
    // reaction logic
  });
  return () => disposer();
}, []);
Enter fullscreen mode Exit fullscreen mode

When Not to Use Local Observable State

  1. State needs to be shared widely across the app - use a global store instead.

  2. Very simple state - useState might be sufficient for trivial cases.

  3. Server-side rendering - Be cautious as local state won't persist between server and client.

Conclusion

MobX local observable state in observer components offers a powerful middle ground between simple useState and full global state management. It brings MobX's reactivity to component-local state while maintaining clean encapsulation. This pattern shines for complex component state, forms, and cases where you want reactive updates without global state overhead.

By understanding when and how to use local observable state effectively, you can write more maintainable, reactive React components with MobX.

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

👋 Kindness is contagious

DEV is better (more customized, reading settings like dark mode etc) when you're signed in!

Okay