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
Related
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
I am trying to check and uncheck a checkbox based on other conditions in screen..
I am printing an element using document.getElementById('foo') this is returning null even my element is present in screen and in DOM.
Please help me to solve problem.
I am developing code in which after API is being fetched state variable need to be set and do other functionality based on the respective state variable.
Is it feasible to do most of the logic inside the call back of the setState to promote synchronus way of coding or any other concepts are present to do the same?
this.setState({
filter:filterValue
},function(){
// Most of the coding logic goes here
})
Please suggest a prominent way if it is wrong
Well you definitely shouldn't use document.getElementById since it's against reactive programming logic but it's hard to say where is a problem if you don't provide example code.
Try to implement checkbox in React way:
const CheckBoxComponent = (isChecked) => {
return <CheckBox checked={isChecked ? true : false}/>
}
then in your return:
<CheckBoxComponent isChecked={yourFunctionWhereYouResolveWheneverIsOrNotChecked}/>
Another point is that you really won't to hold logic in setState callback. I guess you are a beginner. You should get better knowledge of functional programming. It's easier than handling state logic and mutation.
Judjing from your question you want probably something like that:
const yourAsyncCallToApi = async() => {
await someApiCall()
yourFunctionWhereYouResolveWheneverIsOrNotChecked() //it will be called as soon as u got data from api call
}
const yourFunctionWhereYouResolveWheneverIsOrNotChecked = () => {
// handle your conditions and return false or true based on them
}
I am trying to understand how React works with the new Hooks implementation. In this example, I want the browsers to render selected items as I click on the rendered options. But as you can see, it doesn't work.
Here is the example: https://codesandbox.io/s/pjorxzyrx7
Do I have to use the useEffect in this case? Also, as I understand, useEffect couldn't render anything and only return functions. So, what am I missing here?
Thank you!
You're currently mutating the contents of the selected array instead of replacing it. React can't detect a state change when you do this.
Try something like the following:
const handleSelected = item => {
console.log(item);
console.log(selected);
setSelected([...selected, item]);
};
When updating arrays or objects as a part of a state, always make a new copy to assign so that React can properly know when to re-render.
Also, include relevant parts of your code directly in the question in the future, instead of hiding it behind a link (although including a runnable example is great!)
I have a form with multiple controls that saves everything to a variable. Each control has an onChanged function, which runs a state update with that control's new value:
function onChangedValUpdate(newVal){
let fields = clone(this.state.fields);
fields[controlId] = newVal;
this.setState({fields});
}
My controls are dynamically created, and when they are, they run onChangedValUpdate on their initial value, if one is present. The problem is, sometimes a lot of controls are created at once, and React queues up its setStates using the same cloned fields object for each update. The object is not updated between setStates, presumably for similar reasons to this question. This means that, effectively, all but one control's updates are overwritten.
I tried writing an over-smart routine which used setState's callback to only run it if there isn't one already in progress and remember changes made to the fields variable in between setStates, but React went and ran all my queued updates simultaneously. Regardless, the routine felt too contrived to be right.
I'm sure this is a trivial and solved problem, but I can't seem to formulate my question in a Googleable way. How do I chain state updates that happen concurrently, and of which there may be any number?
EDIT For posterity, my solution, thanks to Yuri:
function onChangedValUpdate(newVal){
this.setState( state => {
let fields = clone(state.fields);
fields[controlId] = newVal;
return {fields};
}
}
You could pass a mutation function to setState. This will prevent overwritting on batched updates because every callback will get the most recent previous state.
function onChangedValUpdate(newVal){
this.setState(function(state){
const fields = clone(state.fields)
fields[controlId] = newVal
return {fields: fields}
});
}
Or using object spread and enhanced object literals.
function onChangedValUpdate(newVal){
this.setState(({fields}) => ({fields: {...fields, [controlId]: newVal}}));
}
I'm running into a weird case that only seems to happen upon first loading a component on a heavily based component page (loading 30+ components).
#Component{
selector: <parent-component>
template: `<child-component [myObject]=myObject>
}
export class ParentComponent {
private myObject:DTOValue;
constructor(service:MyService){
service.getDTOValue().subscribe((dtoValue:DTOValue) => {
this.myObject = dtoValue;
});
}
}
#Component{
selector: <child-component>
template: `<div></div>
}
export class ChildComponent {
#Input set myObject(value:DTOValue) => {//do something};
constructor(){
}
}
In this code, the Parent is going to get a value to a child as an input. This value comes from a request at a later time, so when the child is first initialized, the input could be undefined. When the value does get returned from the request and is set on the variable myObject, I'd expect that the child component would receive an input event being triggered. However, due to the timing, it seems like this is not always the case, especially when I first load a page that contains a lot of files being loaded.
In the case that the child component doesn't receive the input, if I click else where on my page, it seems to now trigger the change detection and will get the input value.
The 2 possible solutions I can think of that would require some large code changes so I want to make sure I choose the right now before implement them.
Change the input to be an Subject, so that I push the input value which should ensure that a correct event is triggered(this seems like overkill).
Use the dynamic loader to load the component when the request as return with the correct value (also seems like overkill).
UPDATE:
Adding a plnker: http://plnkr.co/edit/1bUelmPFjwPDjUBDC4vb, you can see in here that the title seems to never get its data bindings applied.
Any suggestions would be appreciated.
Thanks!
If you can identify where the problem is and appropriate lifecycle hook where you could solve it, you can let Angular know using ChangeDetectorRef.
constructor(private _ref: ChangeDetectorRef)
method_where_changes_are_overlooked() {
do_something();
// tell angular to force change detection
this._ref.markForCheck();
}
I had a similar issue, only with router - it needed to do redirect when/if API server goes offline. I solved it by marking routerOnActivate() for check...
When you trigger change detection this way a "branch" of a component tree is marked for change detection, from this component to the app root. You can watch this talk by Victor Savkin about this subject...
Apologize, the issue ended up being my interaction with jQuery. When I triggered an event for a component to be loaded, inside of the jQuery code, it wouldn't trigger the life cycle. The fix was after the code was loaded to then call for a change detection.