DEV Community

张一凡
张一凡

Posted on

I Let AI Code My React App. Here's What Happened

I gave Cursor free reign to build a React app. The result? Surprisingly solid code.

The Setup

I decided to build a project management app using Cursor (AI coding assistant). The goal: see how well AI can handle React state management.

Challenge: Which state library would AI understand best?

The Contenders

  1. Redux - Powerful but verbose
  2. Zustand - Minimal but simple
  3. MobX - Class-based but decorators
  4. easy-model - The unknown

The Test

I gave Cursor the same prompt for each:

"Build a user management page with CRUD operations, search filtering, and pagination."

Let's see what it generated.

Redux Version

Cursor generated:

  • 3 action files
  • 2 reducer files
  • 1 selector file
  • 1 hook file
  • Component code

Then it got confused. Which action does what? Where should this logic go?

Result: ~300 lines, but messy.

Zustand Version

const useStore = create((set) => ({
  users: [],
  loading: false,
  filter: "",
  setUsers: (users) => set({ users }),
  setFilter: (filter) => set({ filter }),
}));
Enter fullscreen mode Exit fullscreen mode

Clean, but Cursor struggled with:

  • Where to put business logic?
  • How to handle async properly?
  • No clear pattern for complex scenarios

Result: ~80 lines, but incomplete.

MobX Version

Cursor loved the class syntax at first:

class UserStore {
  @observable users = [];
  @action async fetchUsers() {
    /* ... */
  }
}
Enter fullscreen mode Exit fullscreen mode

But then TypeScript errors started appearing:

  • "Decorator support is experimental"
  • "Property does not exist on type"
  • "Cannot find name 'observer'"

Result: ~120 lines, but broken.

easy-model Version

class UserModel {
  users: User[] = [];
  loading = false;
  filter = "";

  @loader.load()
  async fetchUsers() {
    this.loading = true;
    const res = await api.getUsers({ filter: this.filter });
    this.users = res.data;
    this.loading = false;
  }

  setFilter(filter: string) {
    this.filter = filter;
    this.fetchUsers();
  }
}
Enter fullscreen mode Exit fullscreen mode

Cursor nailed it. First try. No errors.

Result: ~60 lines, fully functional.

Why easy-model Won

1. Class = AI's Native Language

AI models are trained on OOP code. Classes make sense to them.

class UserModel {
  // properties = state
  users: User[] = [];

  // methods = actions
  async fetchUsers() {
    /* ... */
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Templates Work

Cursor could apply templates:

// CRUD template
class CRUDModel {
  items: Item[] = [];
  @loader.load() async fetchAll() {
    /* ... */
  }
  @loader.load() async create(item: Item) {
    /* ... */
  }
  async update(id: number, item: Item) {
    /* ... */
  }
  delete(id: number) {
    /* ... */
  }
}
Enter fullscreen mode Exit fullscreen mode

3. No Decorator Confusion

No @observable, @action, @observer. Just plain TypeScript.

4. TypeScript Just Works

Full inference, no any, no type gymnastics.

5. DI Makes Dependencies Clear

const apiSchema = object({ baseUrl: string() });

class UserApi {
  @inject(apiSchema)
  config?: { baseUrl: string };
}
Enter fullscreen mode Exit fullscreen mode

Cursor knows what this service needs.

The Verdict

Library Lines Errors AI Understanding
Redux 300+ Few Poor
Zustand 80 None Medium
MobX 120 Many Good
easy-model 60 None Excellent

What Cursor Built

With easy-model, Cursor built:

  • ✅ User CRUD
  • ✅ Search filtering
  • ✅ Pagination
  • ✅ Loading states
  • ✅ Error handling
  • ✅ Cross-component state sharing
  • ✅ Undo/redo capability

All in ~60 lines of readable code.

Conclusion

For AI-assisted coding, easy-model is the clear winner:

  • AI understands class-based models
  • No boilerplate to confuse AI
  • TypeScript works out of the box
  • Templates are learnable
  • Dependencies are explicit

Result: AI produces working code on the first try.


GitHub: https://github.com/ZYF93/easy-model

npm: pnpm add @e7w/easy-model

⭐️ Star if this experiment surprised you too!

Top comments (0)