Есть ли способ заранее не определять модель данных в mobx-state-tree? (для динамического определения типа)

Я хотел бы иметь возможность не определять тип для модели данных, но при этом иметь возможность наблюдать данные после их загрузки. У меня также есть отношения, но они могут быть определены статически. Документы говорят мне о замороженных, но мне нужно, чтобы записи были заметны. Без этого мне было бы лучше придерживаться того, что у меня есть сейчас.

Я прочитал кое-что о типах динамических моделей в комментариях к этому: https://codeburst.io/the-curious-case-of-mobx-state-tree-7b4e22d461f, но поскольку я еще не работал с mst и не использую ts, мне не хватает информации, чтобы увидеть, какое именно решение он имеет в виду.

Что mst хочет, чтобы я сделал:

import React from "react";
import { render } from "react-dom";
import { types } from "mobx-state-tree";
import { observer } from "mobx-react";

const Todo = types.model({
  name: types.optional(types.string, ""),
  done: types.optional(types.boolean, false)
});

const eat = Todo.create({ name: "eat" });
render(
  <div>
    Eat TODO: {JSON.stringify(eat)}
  </div>,
  document.getElementById("root")
);

Что я хочу сделать:

import React from "react";
import { render } from "react-dom";
import { types } from "mobx-state-tree";
import { observer } from "mobx-react";

const Todo = types.model({});

const eat = Todo.create({ name: "eat" });
render(
  <div>
    Eat TODO: {JSON.stringify(eat)}
  </div>,
  document.getElementById("root")
);

больше информации:

https://github.com/mobxjs/mobx-state-tree/issues/326#issuecomment-433906949

https://github.com/mobxjs/mobx-state-tree/pull/247


person ThaJay    schedule 29.10.2018    source источник


Ответы (1)


Вот как это может работать в приложении. В основном это работает, просто добавление и удаление элементов не обновляет компонент.

Я придумал, это может сработать. Это происходит в песочнице. Работа в процессе. Как есть, это не работает, потому что невозможно изменить типы после их инициализации.

С помощью https://egghead.io/lessons/react-create-dynamic-types-and-use-type-composition-to-extract-common-functionality

https://codesandbox.io/s/m39mjomzwx

import React, { Component } from "react";
import { types } from "mobx-state-tree";
import { observer } from "mobx-react";
import { render } from "react-dom";

const ModelActions = types.model({}).actions(self => ({
  addToName: function addToName(string) {
    self.name = `${self.name} ${string}`;
  }
}));

function createModel(instance) {
  return types.compose(
    types.model(instance),
    ModelActions
  );
}

const TodoActions = types.model({}).actions(self => ({
  toggle: function toggle() {
    self.done = !self.done;
  }
}));

function createTodoModel(todo) {
  return types.compose(
    TodoActions,
    createModel(todo)
  );
}

function fetchUsers() {
  return Promise.resolve([{ id: 1234, name: "Jan" }]);
}

function fetchTodos() {
  return Promise.resolve([
    { id: 5, name: "eat", done: false },
    { id: 1, name: "drink" }
  ]);
}

function createDataStore(userData, todoData) {
  const User = createModel(userData[0]);
  const Todo = createTodoModel(todoData[0]);

  return types
    .model({
      users: types.map(User),
      todos: types.map(Todo)
    })
    .actions(self => {
      return {
        addUser(user) {
          self.users[user.id] = User.create(user);
        },
        removeUser(id) {
          self.users.delete(id);
        },
        addTodo(todo) {
          self.todos[todo.id] = Todo.create(todo);
        }
      };
    });
}

function makeStore([userData, todoData]) {
  const store = createDataStore(userData, todoData).create();

  function addData(add, data) {
    for (let i in data) {
      add(data[i]);
    }
  }

  addData(store.addTodo, todoData);
  addData(store.addUser, userData);

  return Promise.resolve(store);
}

const AppState = types.model({ selected: false }).actions(self => {
  return {
    select() {
      self.selected = !self.selected;
    }
  };
});

const appState = AppState.create();

let dataState = null;

// works
const ThingNode = observer(function ThingNode({ thing }) {
  return (
    <span>
      {JSON.stringify(thing)}
      <br />
    </span>
  );
});

function* getThingList(things) {
  for (let key in things) {
    if (Number(key)) {
      yield (
        <ThingNode key={key} thing={things[key]} />
      );
    }
  }
}

// does not add or remove items
const Child = observer(function Child({ state, store, ready }) {
  return (
    <div>
      Users:
      <br />
      {store ? [...getThingList(store.users)] : null}
      <br />
      Todos:
      <br />
      {store ? [...getThingList(store.todos)] : null}
      <br />
      Selected:
      <br />
      {JSON.stringify(state.selected)}
      <br />
      Ready:
      <br />
      {JSON.stringify(ready)}
    </div>
  );
});

@observer
class Parent extends Component {
  state = { ready: null };

  componentDidMount() {
    Promise.all([fetchUsers(), fetchTodos()])
      .then(makeStore)
      .then(state => {
        dataState = state;
        // this.setState({ ready: true });
        this.props.store.select();
      })
      .then(() => {
        // test some stuff
        dataState.addTodo({ id: 789, name: "eat a cake" });

        dataState.addUser({ id: 324, name: "Henk" });
        dataState.users[324].addToName("Klaassie");

        dataState.todos[1].addToName("haha");
        dataState.todos[5].toggle();

        setTimeout(() => {
          dataState.removeUser(1234);
          dataState.addTodo({ id: 90, name: "dinges" });
          dataState.todos[5].addToName("thing");
        }, 1000);

        setTimeout(() => {
          dataState.todos[789].addToName("hihi");
        }, 2000);

        setTimeout(() => {
          dataState.todos[90].addToName("twice");
        }, 4000);
      })
      .then(() => {
        this.setState({ ready: false });
      })
      .then(() => {
        // only now do the added / removed entries become visible
        setTimeout(() => this.setState({ ready: true }), 3000);
      });
  }

  render() {
    console.log("Parent", dataState);

    return (
      <Child
        store={dataState}
        state={this.props.store}
        ready={this.state.ready}
      />
    );
  }
}

render(<Parent store={appState} />, document.getElementById("root"));
person ThaJay    schedule 29.10.2018