Simple MobX Observable not Rendering - reactjs

I have a simple React class annotated with a MobX #observer annotation and a simple data structure (annotated with #observable data = { .. }. An action updates this structure but does render.
Here is source code of the class: -
import React, {Component, Fragment} from 'react';
import {observer} from "mobx-react";
import {observable} from "mobx";
import {withRouter} from 'react-router-dom';
#observer
#withRouter
class UpdateDetailsForm extends Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
#observable data = {
error: ""
};
onClick () {
this.data.error = "error has occurred";
console.log("Something has happened!"); // testing purposes
}
render() {
return (
<div>
<div className="red">[ {this.data.error} ]</div>
<input type="button" value="Click Me!" onClick={this.onClick} />
</div>
);
}
}
However, when I click the button the console displays ...
Something has happened!
... which proves the state of data is mutated but the HTML doesn't update.

Just figured it out! It was the order of the class annotations is important i.e.
#withRouter
#observer
class UpdateDetailsForm extends Component {
...
This works!
But when #withRouter is the closest annotation to class UpdateDetailsForm it fails.
Also found this in MobX docs - https://mobx.js.org/best/pitfalls.html
_#inject('store') before #observer will cause MobX to not trigger_
So the same thing happens for the #inject annotation.

Just check out your mobx version. Before mobx version 6, this will work. From version 6, you need an extra makeObservable(this) in the constructor.
This is the official thread.
MobX before version 6 did not require the makeObservable(this) call in the constructor, but because it makes the implementation of decorator simpler and more compatible, it now does. This instructs MobX to make the instances observable following the information in the decorators -- the decorators take the place of the second argument to makeObservable.

Related

React class instances

I made a dummy React component that does nothing but prints out its this value.
import React from 'react'
import ReactDOM from 'react-dom'
class MyComponent extends React.Component {
constructor() {
super()
}
componentDidMount() {
console.log(this)
}
render() {
return <div />
}
}
const myComponent = new MyComponent()
console.log(myComponent)
ReactDOM.render(<MyComponent />, document.getElementById('app'))
I expected that console.log(myComponent) would produce the same result as console.log(this). However, the latter has additional properties (e.g. _reactInternalFiber) and some properties are different (e.g. context, props).
My question is, how is React or ReactDOM able to provide these additional attributes? You would think that all instances of MyComponent would have the same attributes since they have the same constructor() function, but that is not the case here.

In reactjs, what is wrappedComponent.propTypes?

Below code example is a simplified version a component. I don't understand the code at the bottom of this component, the Case.wrappedComponent.propTypes part. I can't find relevant document about wrappedComponent on Internet as well.
Questions:
What is wrappedComponent and propTypes key in it?
What do they do?
Where can I find document for these things?
import React, { Component } from 'react';
#inject('store') #observer
export default class Case extends Component {
constructor(props) {
super(props);
this.caseId = this.props.match.params.id;
this.setOtherComment = this.setOtherComment.bind(this)
this.submitOtherComment = this.submitOtherComment.bind(this)
}
render() {
return '...'
}
}
Case.wrappedComponent.propTypes = {
store: React.PropTypes.object.isRequired,
match: React.PropTypes.object.isRequired
};
This is an API of mobx-react (with inject) and according to the DOCS
Using propTypes and defaultProps and other static properties in combination with inject
Inject wraps a new component around the component you pass into it. This means that assigning a static property to the resulting component, will be applied to the HoC, and not to the original component
........
if you want to make assertions on the data that is being injected
(either stores or data resulting from a mapper function), the
propTypes should be defined on the wrapped component. Which is
available through the static property wrappedComponent on the inject
component

React alternative coding style with a 'constructor'

Creating a React component the 'standard' way a constructor will run before any rendering and I can use componentDidMount etc to run before the rendering
export class BotShowUI extends Component{
constructor(props) {
super(props)
}
....
My question is in the code below how do I get a constructor type method or another method to run (similar to componentDidMount) before the rendering in the return statement ?
import React, {Component} from 'react';
import PropTypes from 'prop-types';
const BotShowUI = ({ bot, onClick }) => {
return(
<div id={bot.id} onClick={onClick}>
{bot.id} : {bot.text}
</div>
)
}
BotShowUI.propTypes = {
bot: PropTypes.object.isRequired,
onClick: PropTypes.func.isRequired
};
export default BotShowUI;
Currently you cannot. Functional components are stateless. They won't always be, though. https://twitter.com/sebmarkbage/status/658713924607606784

Subclassing react components, HOC or classic OO?

I am in the process of writing a React application which is responsible for rending content from an external CMS.
Content is pulled from the CMS into a redux store when the application first loads and is accessed via connected components or props throughout its life-cycle.
I need certain methods to be available across all components making use of CMS'd content.
After researching the "React" way of achieving this, it seems the following way is generally frowned upon whilst using higher order components is viewed as a better practice. Coming from an OO background, I'm struggling to understand why?
For example...
import React, { Component } from 'react';
class CMSComponent extends Component {
constructor(props) {
super(props);
}
mySharedMethod(path) {
// Assume CMS content is located in this.props.content unless otherwise stated
// Please be aware that the actual code has been simplified massively for the sake of the question
return this.props.content[path]
}
}
I now have a base class that all my CMS components can inherit from, like so...
import CMSComponent from './components/cms'
class Page1 extends CMSComponent {
constructor(props) {
super(props);
}
render() {
// mySharedMethod() inherited from base class
return <div>{ this.mySharedMethod(['data', 'intl', 'my-keyed-content']) }</div>
}
}
If anyone could shed any light on why this is considered incorrect I would be extremely grateful.
For reference, I guess the same scenario using HOC would look something like this...
import React, { Component } from 'react'
export default function CMSInject(ChildComponent) {
class CMSComponent extends Component {
constructor(props) {
super(props);
}
mySharedMethod(path) {
return this.props.content[path]
}
render() {
return <ChildComponent {...this.props} {...this.state} /* and some refs */ />
}
}
return CMSComponent
}
...then export the child via the higher order parent component...
import React, { Component } from 'react'
class Page1 extends Component {
constructor(props) {
super(props);
}
render() {
// mySharedMethod() still inherited from base HOC class
return <div>/* CMS BASED CONENT HERE */</div>
}
}
export default CMSInject(Page1)

Click event in ReactJS error: imports/ui/ParentComponent.jsx:5:16: Unexpected token (5:16)

I am trying to learn Event in ReactJS.
I created 2 components
ChildComponent is
import React, { Component } from 'react';
// App component - represents the whole app
export default class ChildComponent extends Component {
render() {
return (
<button onClick={this.props.onBannerClick}>Click me!</button>
);
}
}
And ParentComponent is
import React, { Component } from 'react';
import ChildComponent from './ChildComponent.jsx'
// App component - represents the whole app
export default class ParentComponent extends Component {
performMagic: function() {
alert('TAADAH!');
},
render() {
return (
<BannerAd onBannerClick={this.performMagic} />
);
}
}
but I got the error
Errors prevented startup:
While building for web.browser:
imports/ui/ParentComponent.jsx:5:16: Unexpected token (5:16)
Your application has errors. Waiting for file change.
I think the error is from
performMagic: function() {
alert('TAADAH!');
},
But I do know what the error is.
By the way, can anybody recommends me good debug tools for ReactJS?
Because you're using the ES6 syntax you'll have to bind the function to the instance using the following approach.
constructor(props) {
super(props)
this.performMagic = this.performMagic.bind(this)
}
This will allow you to use the this keyword in the onClick call
import React, { Component } from 'react';
import ChildComponent from './ChildComponent.jsx'
// App component - represents the whole app
export default class ParentComponent extends Component {
constructor(props) {
super(props)
this.performMagic = this.performMagic.bind(this)
}
performMagic() {
alert('TAADAH!');
}
render() {
return (
<BannerAd onBannerClick={this.performMagic} />
);
}
}
Need to write:
performMagic () {
alert('TAADAH!');
},
You need to use new sintax for functions, when write class which is new sintax.
EDIT: You can use "React Developer Tools" chrome extension and gaearon "redux-devtools" for development.
You need to use the new ES6 syntax when making your React Component a class. Use
performMagic() {
alert('TAADAH!');
}
make sure you don't put a comma after the function

Resources