7

in my react application (with typescript) I want to use React hooks (specifically useState) to manage the form state and meanwhile use it as an observable component for Mobx store but I get the error

Hooks can only be called inside the body of a function component.

so for instance in the following component

import * as React from "react";
import { inject, observer } from "mobx-react";
import { MyStore } from "./MyStore";

interface IProps {
  myStore?: MyStore;
  id: string;
}

const MyComponent: React.FC<IProps> = props => {
  const [state, setState] = React.useState("");
  return (
    <div>
      <h1>{props.id}</h1>
    </div>
  );
};
export default inject("myStore")(observer(MyComponent));

I saw a solution but that was using React.createContext for exporting the store class. is not where the old approach for Mobx and Hooks?

here is the sanbox for the example

Amir-Mousavi
  • 4,273
  • 12
  • 70
  • 123
  • 3
    `mobx-react@5` does not support hooks, but [version 6 will support it, which is in `rc.4` right now](https://codesandbox.io/s/zk7555yz0l). – Tholle Mar 25 '19 at 08:28
  • @Tholle, Thanks. BTW as I see you are so active here especially for react tag, do you think as using context became much easier since 16.8, is it more convenient to build custom state manager for small to mid-size apps, rather than importing some MB of library? – Amir-Mousavi Mar 25 '19 at 09:35
  • You could definitely use e.g. the [`useReducer`](https://reactjs.org/docs/hooks-reference.html#usereducer) hook and share the state object and the dispatch function through context, which will be suitable for a lot of use cases, but I don't think it will replace MobX for mid-sized apps. It depends a lot on your particular project. – Tholle Mar 25 '19 at 09:39

2 Answers2

8

Thanks to @Tholle mentioning the Mobx version, now that the Mobx 6 is released this question is solved

Amir-Mousavi
  • 4,273
  • 12
  • 70
  • 123
6

mobx-react doesn't support hooks and if you wish to use hooks with mobx you need to make use of mobx-react-lite which is also mentioned in the github documentation

To do that you can make use of React.createContext instead of provider and useContext instead of inject

Index.tsx

import * as React from "react";
import { render } from "react-dom";
import MyComponent, { Store } from "./MyComponent";

import "./styles.css";
import MyStore from "./MyStore";

function App() {
  const [state, setState] = React.useState("");
  return (
    <Store.Provider value={MyStore}>
      <div className="App">
        <MyComponent id={"someID"} />
      </div>
    </Store.Provider>
  );
}

const rootElement = document.getElementById("root");
render(<App />, rootElement);

MyComponent.tsx

import * as React from "react";
import { Observer } from "mobx-react-lite";
import { MyStore } from "./MyStore";

interface IProps {
  myStore?: MyStore;
  id: string;
}

export const Store = React.createContext();
const MyComponent: React.FC<IProps> = props => {
  const [state, setState] = React.useState("");
  const store = React.useContext(Store);
  console.log(state, store);
  return (
    <div>
      <h1>{props.id}</h1>
    </div>
  );
};
export default MyComponent;

Working demo

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400