Search code examples
reactjstypescriptmobx-reactreact-tsx

React with Typescript: Type missmatch


its a while started using React with typescript but always getting confused with types and what React element expects.

like in this scenario (Also sandbox here) I get the follwing error for Todos Component

in VSCODE with prettier and TSLint setup: Type '(props: PropsWithChildren) => Element[]' is not assignable to type 'FunctionComponent'.

in Snadbox: Type '(props: IProps) => void[]' is not assignable to type 'FunctionComponent'.


    import { TodoStore } from "./stores/TodoStore";
    import { observable } from "mobx";
    interface IProps {
      todoStore: TodoStore;
    }
   // red underline at `TodoComp` with mentioned error message
    const TodosComp: React.FC<IProps> = props => {
      const { todos } = props.todoStore;
      return todos.map(todo => {
        <div key={todo.id}>
          <p>{todo.title}</p>
          <select />
        </div>;
      });
    };
    export default inject("todoStore")(observer(TodosComp));

The Mobx store for todos is like

import { decorate, observable, runInAction, action } from 'mobx';
export interface ITodo {userId: number; id: number; title: string; completed: boolean;
}
export class TodoStore {
  public todos: ITodo[];
  constructor() {
    this.todos = [];
    this.fetchTodos();
  }

  public async fetchTodos() {
    const response = await fetch('http://jsonplaceholder.typicode.com/todos');
    const todos: ITodo[] = await response.json();
    runInAction(() => {
      this.todos = todos;
    });
 }}
decorate<TodoStore>(TodoStore, { todos: observable, fetchTodos: action });

Is the problem from Mobx and if the class itself is used as a type in the component? does mobx classes need a separate interface?

Also as a Mobx specific question is the way in sandbox correct to instantiate the store class at provider?

in general, if somebody knows a good article about React with typescript how to manage props and JSX types would be appreciated.


Solution

  • Function should return a JSX.Element and you need to pass a div or a Fragment, this should works.

    const Todos: React.FC<IProps> = (props: IProps): JSX.Element => {
      const { todos } = props.todoStore;
      return (<React.Fragment>
        {todos.map(todo => {
          <div key={todo.id}>
            <p>{todo.title}</p>
            <select />
          </div>;
        })}
      </React.Fragment>);
    };