TypeError when using `useLocalObservable` from MobX to work with TypeScript?

Viewed 1263

I'm basically following this video which points to this repo to convert my app to use MobX v6. I'm using useLocalObservable as useLocalStore is deprecated in MobX v6.

The error I get is on FrameItStore inside of useLocalObservable:

Argument of type 'typeof FrameItStore' is not assignable to parameter of type '() => Record<string, any>'.
  Type 'typeof FrameItStore' provides no match for the signature '(): Record<string, any>'.ts(2345)

And then the whole return statement is in red squiggly lines in VSCode.

VSCode Mobx TypeScript Error

Would love to know how to fix it?

types.ts

export type Window = {
  width: number
  height: number
}

export type Point = {
  x: number
  y: number
}

export type TrafficSignal = {
  close: string
  minimize: string
  maximize: string
}

export type TrafficSignalPosition = {
  close: Point
  minimize: Point
  maximize: Point
}

export interface IFrameItStore {
  id: number
  trafficSignalColors: Array<TrafficSignal>
}

FrameItStore.ts

import { makeObservable, observable, action, computed } from 'mobx'

import type {
  TrafficSignal,
  Point,
  Window,
  TrafficSignalPosition,
  IFrameItStore,
} from '../types'

export class FrameItStore implements IFrameItStore {
  id = 0
  trafficSignalColors = [
    {
      close: '#EF4444',
      minimize: '#FBBE25',
      maximize: '#49DE80',
    },
    {
      close: 'black',
      minimize: 'blue',
      maximize: 'orange',
    },
  ]

  constructor() {
    makeObservable(this, {
      win: observable,
      box: observable,
      boxCenter: computed,
      trafficSignalPosition: computed,
      trafficSignalColors: observable,
      id: observable,
    })
  }

  win(): Window {
    return {
      width: window.innerWidth,
      height: window.innerHeight,
    }
  }

  box(): Window {
    return {
      width: 1024,
      height: 600,
    }
  }

  get boxCenter(): Point {
    return {
      x: (this.win().width - this.box().width) / 2,
      y: (this.win().height - this.box().height) / 2,
    }
  }

  get trafficSignalPosition(): TrafficSignalPosition {
    return {
      close: { x: this.boxCenter.x + 20, y: this.boxCenter.y + 20 },
      minimize: { x: this.boxCenter.x + 2 * 20, y: this.boxCenter.y + 20 },
      maximize: { x: this.boxCenter.x + 3 * 20, y: this.boxCenter.y + 20 },
    }
  }
}

export const config = new FrameItStore()

FrameItContext.tsx

import * as React from 'react'
import { useLocalObservable } from 'mobx-react'

import { FrameItStore } from './FrameItStore'

const FrameItContext = React.createContext(null)

export const FrameItProvider = ({ children }: { children: React.ReactChild }) => {
  const frameItStore = useLocalObservable(FrameItStore)

  return (
    <FrameItContext.Provider value={frameItStore}>
      {children}
    </FrameItContext.Provider>
  )
}

export const useFrameItStore = () => React.useContext(FrameItContext)
1 Answers

The repo you are following returns an object that is used for useLocalStore. An equivalent in your code would be to use an instance of your FrameItStore class for useLocalObservable.

You can also return this instance from a function given to useLocalObservable to only create a single instance once on the first render of the component.

const FrameItContext = React.createContext<IFrameItStore | null>(null);

export const FrameItProvider = ({ children }: { children: React.ReactChild }) => {
  const frameItStore = useLocalObservable(() => new FrameItStore());

  return (
    <FrameItContext.Provider value={frameItStore}>
      {children}
    </FrameItContext.Provider>
  );
};
Related