You can look at the issue via codesandbox
Codesandbox: https://codesandbox.io/s/great-frog-m2s7h
Context
I am trying to fetch some data and populate some variable and render the value in React, essentially following the documentation await/async + runInAction example: https://mobx.js.org/actions.html#asynchronous-actions
However, when the data is fetched, React actually does not re-render. However, if you edit the text in there(i.e. change hi to his or whatever, then you see the correct value appear.
Problem
What exactly am I doing wrong with the data fetching? Why isn't the observable value not being re-rendered correctly when its been assigned a value after some data has been fetched?
This is actually one of the limitations of Mobx.
From the docs:
make(Auto)Observable only supports properties that are already defined. Make sure your compiler configuration is correct, or as workaround, that a value is assigned to all properties before using make(Auto)Observable. Without correct configuration, fields that are declared but not initialized (like in class X { y; }) will not be picked up correctly.
Just initialize the title this.title=undefined and it will work.
Its a simple mistake. MobX can not compare data of title to something that dosent exist. Stuff should have default value of some sort, even a null. hence in a constructor you need to define title default value like
constructor() {
this.title = [],
makeAutoObservable(this);
}
or, if you wish, even null
constructor() {
this.title = null,
makeAutoObservable(this);
}
Basiclaly, whenever you create some observable variable in a store - you need to define its default value in a constructor above makeAutoObservable.
Here, a forked project of yours with juswt this 1 line change to make it work https://codesandbox.io/s/suspicious-noether-fnhjw
Related
Suppose I have something like this where both components A and D listen to changes in a global store :
import React from 'react'
import useStore from 'whatever-global-store-manager'
function A() {
const [store] = useStore()
if(!store.currentUser)
return <h1>You must log in</h1>
else return <B/>
}
function B() {
return <C/>
}
function C() {
return <D/>
}
function D() {
const [store] = useStore()
console.log(store.currentUser) // Can it be falsey ?
return <h1>{store.currentUser.name}</h1>
}
In A, when currentUser is falsey, B is not rendered, thus D is not rendered. But suppose this scenario :
At first, currentUser is defined as an object with a name property, so D renders, listens to changes in the store and renders the name.
Then, somewhere else in the app, currentUser is set to null.
In which order are the "listeners" processed ? Is there any chance that function D is executed with currentUser to null even when begin ultimately removed from the component tree ?
Another way to formulate the question : Should I check against currentUser in component D before accessing its name property ?
I was looking in the doc for a rule like "When two components listen to the same event, the one higher in the hierarchy is rendered first, and if it turns out the second one should be unmounted according the first's output, then the second one is never even called", but couldn't find anything. In practice, I know it works, but I would like to be sure that it's not just luck.
I believe this largely depends on the store observer mechanism, so it's hard to give a conclusive answer without knowing which store you're using. If observers are registered in order, that might affect how you need to deal with it.
If you wanna find out for sure, you could console.log your render methods, or use debugger while changing the value of currentUser.
Analysis of a hypotetical implementation: let's say an observer is registered when the component mounts, and unregistered when it unmounts. In this situation, the component A would trigger first (since it was registered first), and cause D to unmount, unregistering his trigger. In this hypothetical scenario, D wouldn't need to check for null.
Unrequested advice: a good thing for you might be centralizing the "data collection" in one parent component, while the children just receive that as props and render (without observing the store). I've found (both from lore and personal experience) that it simplifies a lot the development process.
Another way to formulate the question :
Should I check against currentUser in component D before accessing its name property ?
Yes, it is definitely a good decision: it is preferable that there is one redundant code line, instead of obtaining an error.
I was looking in the doc for a rule like
"When two components listen to the same event,
the one higher in the hierarchy is rendered first...
I think the opposite. Although I neither could find the specific documentation to explaine it, I remember that Components do not update like a cascade. That is the idea of the component oriented programming: each one is an independent entity.
Note: if I understand your example well, you could test this example by adding a setTimeout that wraps the return of function A, right? So this way you can then set currentUser as null and D wil be still rendered and you can see what happens.
I'm new to react an got a question about components.
I made a component like this:
class HeaderLabel extends React.Component {
constructor() {
super();
}
componentWillMount() {
this.setState({ value: String(this.props.value) });
}
render() {
return (
<div>
{this.props.value && this.props.value != "" ? <label className="control-label">{this.props.value}</label> : <label className="text-muted">({Translation.getPhrase("EMPTY")})</label>}
</div>
);
}
}
HeaderLabel.propTypes = {
value: PropTypes.string
}
export default HeaderLabel;
As you can see I'm using this.props.value to display the value. Is this the right way or is it better to use this.state.value?
Short answer: Use this.props.value.
Since you are taking the value directly from the props without mutating it somehow, there is no benefit at all in first storing it in the state and then using this.state.value.
Basically this boils down to the fundamental idea of React, which is that you should only ever have "one source of truth". By having value both in your props and in the state you have two sources of truth. As a developer, you should try to have a "master state" which passes the data down to its components.
Sometimes, however rarely, it can be a good idea to store the prop value directly to your state. For example, you might want to have a parent component which holds a string which is displayed in your app. This string can be modified in a modal which is opened when clicking the appropriate button. The "temporary under-edit" string would be in the modal-state, whereas the previously saved one still in the master component.
You might find this official blog article helpful: You Probably Don't Need Derived State
In this example, you are using the props value to display the data which you have received, then using this.props.value makes sense. Props are read-only, means the child is not the owner of that particular data and hence it cannot change anything on that data.
when to use this.state.value, let say a scenario where you get data from parent and you have to do some manipulations on that data before displaying, then, in that case, take props into a state and do the manipulations.
you can use both this.props.value and this.state.value. But first you must answer the following question. Depending on the answer you choose appropriate way of setting the value.
Which component handles the subsequent change of value received from this.props.value:
Change of value is coming from Parent component.Then you should use this.props.value.There is no need for you to assign this.props.value to this.state.value. Because the state is maintained inside parent component.
2.Change is handled by HeaderLabel component itself. Then you should set this.state.value.
I am coming from angular world. And I am still kinda new to react.
One question that I encountered is that:
In angular, we have $watch to watch one scope variables to change and update other variables. like watch B,C,D, and change of B,C,D will affect variable A
In react, I am trying to do the same thing. However, I am calling to setState(B,callbackB) to update A. A has a setState that has an impact in render
It seems like doing such works correctly for variable B. However, updating A will occur in next render cycle. And this.forceUpdate() doesn't seems work.
Is there anything that I am doing wrong?
Thanks
An example would help clarify your situation, but depending on what "updating A" entails, you could move it to your render function. E.g.
class Component extends React.Component {
handleUpdate(B) {
this.setState({B})
}
computeAFromB(B) {
...
return A
}
render() {
const A = this.computeAFromB(this.state.B)
return <div>{A}</div>
}
}
When transitioning from Angular to React, it's important to keep in mind that scope !== state. This may be helpful: https://facebook.github.io/react/docs/thinking-in-react.html
You cannot rely on setState to update instantly. Try passing the new value explicitly to setState A.
Here's my example code, I think I found an answer already.
https://jsfiddle.net/yoyohoho/Lo58kgfq/1/
in the example code in jsfiddle. The input field is a required field.
And the button should be disabled if there is nothing in the field.
The problem is that if you type something and you erase it, button is still active, until you type in 1 more key, which is next cycle.
I use input1Err to determine if formError should be disabled in checkErr()
checkErr(){
if(this.state.input1Err == false){
this.setState({formErr: false });
}else{
this.setState({formErr: true });
}
}
This is the reason that why the view change happen in next cycle. Because setState is asynchronous
I was using this.state.input1Err to determine formErr, which may yet be changed before the if statement there.
So I changed to the following closure, which solves my issue.
checkErr(){
const self = this;
(function(j){setTimeout(function(){
if(self.state.input1Err == false){
self.setState({formErr: false });
}else{
self.setState({formErr: true });
}
},10)})(self);
}
This seems be a quick fix. However, what if there is ajax call and hooked up to state.input1Err. Is there a way I can watch this variable, so when it changes complete, I can have some sort of callback?
Thanks
I have been struggling with trying to migrate my React code from ES5 to ES6. As I have found out by now, this is no longer automatically bound which causes all sorts of hell.
I am trying to figure out by trial and error what objects are being passed around. So far I can find everything and adjust accordingly. However when it comes to this.setState I am having problems because it is not visible in console.log!!!! See screenshot in ES5:
and here is the same kind of code in ES6:
Please teach me how to fish i.e. help me figure out how to understand when an object has this.setState or not?
things i have tried
from googling around i understand you might be able to default bind everything by changing the base component. unfortunately this did not work when it came to this.setState. It looks identical to the ES5 version of this in console so I concluded that setState is still not being bound somehow
To oversimplify how this works in JS:
If you call a function as an object method (e.g., instance.foo()) then this refers to that object instance.
If you call a function by itself (e.g., foo()), then this is either undefined or the global object, depending on whether strict mode is in effect.
If you take a reference to an object method then call it, that means you're calling it by itself, even though it was originally an object method. (E.g., var bar = instance.foo; bar();.
Again, this is an oversimplification; MDN has details.
As this applies to React, and as explained in the React documentation on "Handling Events":
You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.
In your code, you render your RawInput as
<RawInput value={this.state.value} updateValue={this.updateValue}/>
You're passing a reference updateValue function in as a simple function, so this will not be bound within updateValue.
Basically, any time you pass a function as a React prop, unless you've bound it yourself, it's likely an error. The symptom is typically that this is undefined. In your code, it's a little more complicated:
this.props.updateValue(modifiedValue);
The RawInput's updateValue property is the unbound function App.updateValue, but because you're invoking it as this.props.updateValue, it's being called as if it were a method of this.props - so this refers to the RawInput's props. That's why your console.log is showing an object with only two properties (start and updateValue): it isn't that setState isn't bound or went away, it's that updateValue wasn't bound, so this isn't what you expect within updateValue.
To fix the issue, as the React docs explain:
Use a fat arrow function: updateValue={(value) => this.updateValue(value)}
Use the experimental property initializer syntax: Replace updateValue(modifiedValue) {...} with updateValue = (modifiedValue) => {...}.
Not mentioned in the React docs, but an approach I often use: Bind updateValue yourself. For example:
constructor(props) {
super(props);
this.updateValue = this.updateValue.bind(this);
}
you can replace console.log with this:
console.shallowCloneLog = function(){
var typeString = Function.prototype.call.bind(Object.prototype.toString)
console.log.apply(console, Array.prototype.map.call(arguments, function(x){
switch (typeString(x).slice(8, -1)) {
case 'Number': case 'String': case 'Undefined': case 'Null': case 'Boolean': return x;
case 'Array': return x.slice();
default:
var out = Object.create(Object.getPrototypeOf(x));
out.constructor = x.constructor;
for (var key in x) {
out[key] = x[key];
}
Object.defineProperty(out, 'constructor', {value: x.constructor});
return out;
}
}));
}
any way, regarding your question, you can add a method like this:
updateValue = () => {...}
in m POV - es6 is cool and great. React components by es6' classes are useless. stick with createClass and you'll be fine (and have mixins if you want!)
Try Object.prototype.hasOwnProperty(). For example:
var X = function() {};
X.prototype.setSomething = 'a';
var x = new X();
x.setSomething; // log 'a' here
x.hasOwnPrperty('setSomething') // log false here
In your case, just console.log(this.hasOwnProperty('setState')).
You have to bind your updateValue function with the component in order to have the correct context (this).
In your case, your parent class BaseComponent allows you to use the herited method _bind like that :
class App extends BaseComponent {
constructor(props){
super(props);
this.state={value:'start'};
this._bind('updateValue');
...
I have some object in my Component:
this.user = Authentication.user;
Which works just fine - it copies the reference, and if Authentication.user changes, this.user in my Component changes as well.
However, I am wondering, if it is possible to do the following:
this.user = Authentication.getUser()
where Authentication.getUser :
getUser(){
return this.user;
}
However, this does not seem to copy reference to user from Authentication.
Am I doing something wrong here, or it not possible?
UPDATE
Actually, it works pretty nice:
http://plnkr.co/edit/ULuSl6CIBCzi7gcsLu3R?p=preview
There is no way to fix this for primitives.
TypeScript transpiles to JavaScript and you can't change how JavaScript handles primitive types.
If you want to synchronize, create an Observable that sends value changes, then the receiver can subscribe and gets notified about changes and update his copy of the value.