Reputation: 3774
I have a store that performs a fetch
to get graph data from my server using asyncAction
from mobx-utils.
It looks like this:
class GraphStore {
@observable
public loading: boolean;
@observable
public datapoints: any[];
@asyncAction
*fetch(id: string) {
const datapoints = yield fetch('/api/datapoints');
this.loading = false;
this.datapoints = datapoints;
}
}
In my component I use it like so:
@inject(STORE_GRAPH)
class Graph {
componentWillMount() {
const graphStore= this.props[STORE_GRAPH] as GraphStore;
const { id } = this.props;
graphStore.fetch(id);
}
render(){
const graphStore= this.props[STORE_GRAPH] as GraphStore;
if(graphStore.loading)
return <h2>Loading</h2>
return (
<Chart datapoints={graphStore.datapoints}/>
);
}
This works great but I don't know what to do when I want to expand this to display 2 graphs on the same page? Basically I want to have a parent component like so:
render() {
return (
<Graph id="foo"/>
<Graph id="bar"/>
);
}
Based on this code, the same graph store is being injected into both components, causing 2 fetches to go out and both graphs end up with the same datapoints - whichever one comes in last.
Whats the proper way to do this? Am I just thinking about this the wrong way?
Upvotes: 0
Views: 81
Reputation: 10219
There are many ways to do it, but I would utilize the object oriented nature of MobX, and create one data store, that is instantiated and passed down as a provider to all components. You can see this as your "local db" if you will.
Then just add methods on that data store to fetch and create different instances of Graphs.
Here's some example code (no typescript)
// stores/data.js
import Graph from './Graph';
class DataStore {
@observable graphs = observable.map();
@action getGraphById(id) {
if (!this.graphs.has(id)) {
this.graphs.set(id, new Graph(id))
}
return this.graphs.get(id);
}
}
export default new DataStore();
Then create an instantiable Graph object
// stores/Graph.js
export default class Graph {
@observable id;
@observable loading = false;
@observable datapoints = [];
constructor(id) {
this.id = id;
if (!this.hasData) {
this.fetch();
}
}
@computed get hasData() {
return this.datapoints.length;
}
@action async fetch() {
this.loading = true;
const datapoints = await fetch(`/api/datapoints/${this.id}`);
this.loading = false;
this.datapoints = datapoints;
}
}
In your component tree you'd pass down the dataStore through the provider
import dataStore from './stores/data'
<Provider stores={{ data: dataStore }}>
<Graph id="foo" />
<Graph id="bar" />
</Provider>
Then just use the id prop in the component to initiate the fetch
@inject('data')
@observer
class Graph extends Component {
@observable graph;
componentWillMount() {
const { id, data } = this.props;
this.graph = data.getGraphById(id);
}
render() {
if (this.graph.loading) {
return <h2>Loading</h2>
}
return (
<Chart datapoints={this.graph.datapoints} />
);
}
}
Upvotes: 1