Reputation: 681
I have a store object and i want to populate one of it's properties (which is an empty array) with instances of another object. And i want one of my react component automatically updated when part of the mentioned array instance is changed.
By logging out the value of this constructed store object i can see the change in the browser console, but it doesn't get updated automatically in the react component when its value changed.
So i'd like to get hints, examples of how to implement a solution like this in general.
I want to create a MobX store called Session which would store everything my react webapp needs. This webapp would be a document handling tool, so after creating new or loading existing documents i want to add the Document object to the Session (into an object array called documents). Some more details: a Document consists of one or more section. So using a WYSIWYG editor i add its content to the given section every time it's content changes.
I can add a new Document to the Session, i can update section as well(I can log out the value of a section in console), but using the Session reference to this document and section in a react component it doesnt update its state when section value is changed. To my understanding in my example the reference of a Document is not changed when the value of a section is changed and hence it doesn't trigger MobX to react.
I started to dig in the deep, dark web and found this article. So i started getting excited since asStructure (or asMap) seemed to solve this issue, but it looks like asStructure in MobX is deprecated. Then i found this issue thread, where a thing called observable.structurallyCompare is mentioned. But again i found nothing about this in MobX documentation so im puzzled how to implement it.
So im stuck again and have no idea how to solve this problem.
This is how i reference to the mentioned Session value in the main react component:
import userSession from '../client/Session';
import {observer} from 'mobx-react';
@observer class App extends React.Component {
...
render() {
return (
...
<div>{JSON.stringify(userSession.documents[0].content.sections)}</div>
...
This is how i update the section in the editor react component:
import userSession from '../../client/Session';
...
handleChange(value,arg2,arg3,arg4) {
this.setState({ content: value, delta: arg4.getHTML()});
userSession.documents[0].updateSectionContent(this.props.id, this.state.delta);
}
}
...
Session.js excerpt:
class Session {
constructor(){
extendObservable(this, {
user: {
},
layout: {
},
documents: []
})
//current hack to add an empty Document to Session
this.documents.push(new Document());
}
//yadayadayada...
@action addNewSection() {
userSession.documents[0].content.sections.push({
type: "editor",
id: userSession.documents[0].getNextSectionID(),
editable: true,
content: "",
placeholder: "type here to add new section..."
});
}
}
var userSession = window.userSession = new Session();
export default userSession;
Document.js
import {extendObservable, computed, action} from "mobx";
import userSession from "./Session";
class Document {
constructor(doc = null) {
if (doc == null) {
console.log("doc - no init value provided");
extendObservable(this,{
labels: {
title: "title",
description: "description",
owners: ["guest"]
},
content: {
sections: [
{
type: "editor",
id: "sec1",
editable: true,
placeholder: "this is where the magic happens"
},
]
}
})
} else {
console.log("doc - init values provided: ");
extendObservable(this,{
labels: doc.labels,
content: doc.content
});
}
}
getNextSectionID(){
return `sec${this.content.sections.length}`;
}
@action updateSectionContent(sectionID, delta) {
console.log("doc - looking for section " + sectionID + " to update with this: " + delta);
let index = this.content.sections.findIndex(
section => section.id === sectionID
);
userSession.documents[0].content.sections[index].content = delta;
}
}
export default Document;
Ps.: atm moment i don't remember why i made Document properties observable, but for some reason it was necessary.
Upvotes: 0
Views: 3837
Reputation: 2522
Unfortunately, you are implementing mobx
with React the incorrect way. To make it more comprehensible, I suggest you look into the implementation of the HOC observer
that mobx-react
provide.
Basically, what this HOC does is to wrap your component inside another React component that implement shouldComponentUpdate
that check when the props referred inside render
function change, and trigger the re-render. To make React component reactive to change in mobx
store, you need to pass the store data to them as props
, either in React traditional way, or via the inject
HOC that mobx-react
provide.
The problem while your code does not react to change in the store is because you import the store and use them directly inside your component, mobx-react
cannot detect change that way.
To make it reactive, instead of import it and use it directly in your App
component, you can pass the store as a prop as follow:
@observer
class App extends React.Component {
...
render() {
return (
...
<div>{this.props.sections}</div>
...);
}
}
Then pass the store to App
when it's used:
<App sections={userSession.documents[0].content.sections} />
You can also have a look at how to use inject
here: https://github.com/mobxjs/mobx-react.
An just a suggestion: before jumping directly on some best pattern
, try to stick with the basic, standard way that library author recommend and understand how it works first, you can consider other option after you got the core idea.
Upvotes: 0