State ReactJS and render method - javascript

I'm studying chapter State and lifecycle in reactJS with Clock class and I don't understand why I can re-render my variables "myFirstNumber, mySecondNumber, myWord" which are not states when I use this code in CodePen:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date(),
};
this.mySecondNumber = 0;
}
componentDidMount() {
this.myFirstNumber = 0;
this.myWord = "Start";
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date(),
});
this.myFirstNumber += 1;
this.mySecondNumber += 1;
if (this.myFirstNumber ===5) {
this.myWord = "Finish";
}
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
<h2>myFirstNumber from ComponentDidMount: {this.myFirstNumber}</h2>
<h2>mySecondNumber from constructor: {this.mySecondNumber}</h2>
<h2>myWord: {this.myWord}</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
The render method render all DOM each seconde for my variables which are not states ?

When you call this.setState function, the DOM gets rerendered. Here the DOM is getting updated every second when this.state.date gets changed through this.setState, showing new values. However, if you keep date outside of state, you can see that the DOM no longer rerenders, same result if you want to change "myWord" or "firstNumber" without changing anything in state.

When you run tick() the setState part is what triggers the re-render and that will process everything, state or not.

In componentDidMount() you schedule interval which triggers tick() function every second. Inside this function you update component's state by invoking setState() and passing current time as a new state value. Moreover, you modify some local variables of the class. When arguments passed to setState() are processed by JS engine, what happens asynchronously, not immediately after setState() is called, the component updates its state.
Then the render() function is called by the framework. The render() function returns output which reflects the current values of all variables requested inside render() function. If you didn't call setState() inside tick() method then you wouldn't see any changes even though you modify myFirstNumber and other variables after each second.

Related

react render called before componentdidmount

In my componentDidMount() I am making an API call "invokeservice" to fetch some data, this call then sets a state object that I use in my render.
The problem I am having is that the invokeservice is not being called and set the first time that the page is loaded, however when I refresh the page the invokeservice returns the data fine and the rest of the script will run. It's only the first time that it returns an null value.
Is it possible to call this invoke service in render function and keep the data ready and display it when user tries to load this page.
TRIED:
1.read that ComponentDidMount is called only after 1st initial render.
2. {this.invokeservice()} i tried to call in render function before return which did initial render but after 5sec it was blank and then again 5sec later it is filled again with values.
render function
public render() {
return (
<div className="monitor">
<div className="monitor__table">
{this.monitorrow(translate("cpu"), this.state.cpuString)}
{this.monitorrow(translate("memory"), this.state.pmemoryString)}
</div>
</div>
);
}
constructor
constructor(props) {
super(props);
this.state = {
cpu: 0,
cpuString: ""
};
setInterval(() => this.invokeservice(), 5000);
}
componentdidMount
public componentDidMount() {
this.invokeservice();
}
invokeservice
private invokeservice() {
var client = new HttpClient();
var url = this.props.baseUrl + '//getMonitorUsage' + '?NC=' + Date.now().toString();
client.get(url, (response) => {
this.setState({
cpu: JSONResponse.GetSystemStateResult.CPUusage
});
}
}
});
}
function
monitorrow(left,right) {
return (
<div className="table__row">
<div className="table__cell__left">
<Text>{left}</Text>
</div>
{ right &&
(<div className="table__cell__right">
<Text>{right}</Text>
</div>)
}
</div>
);
}
It is expected.
From the react docs:
These methods are called in the following order when an instance of a component is being created and inserted into the DOM:
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
Ref: https://reactjs.org/docs/react-component.html
There is componentWillMount which will be called before render(). But is it not advised to be used. Read more on the official docs https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

Auto Update value in react component

I'm trying to build a component with auto-updating value based on cookies:
let cookies = 0;
(function count() {
cookies = document.cookie.split("?");
setTimeout(count, 10);
return cookies;
})();
class CartButton extends React.Component {
state = {quantity: cookies.length}
render() {
return (
<Cart onClick={e=>{show_cart()}}>
<Mfont>{this.state.quantity}</Mfont>
<Icon>shopping_cart</Icon>
</Cart>
);
}
}
'count' function works as expected, component is rendered with the latest value returned. Unfortunately, it does not auto-update when 'cookies' are changed. It returns this error:
Warning: render(...): Replacing React-rendered children with a new root component. If you intended to update the children of this node, you should instead have the existing children update their state and render the new components instead of calling ReactDOM.render.
I have tried various variations here but still can't figure it out :/
componentDidMount will get execute only once when your component loads first time. This is the correct place to write any logic which we need to execute after page load.
Try this,
class CartButton extends React.Component {
//It is good to have a constructor for the component which has state
constructor(props){
super(props);
this.state = {quantity: cookies.length}
this.updateQuantity;
}
componentDidMount(){
this.updateQuantity = setInterval(()=> {
cookies = document.cookie.split("?");
this.setState({quantity: cookies.length})
},10)
}
//Don't forget to clear any setInterval like below
componentWillUnmount(){
clearInterval(this.updateQuantity);
}
render() {
return (
<Cart onClick={e=>{show_cart()}}>
<Mfont>{this.state.quantity}</Mfont>
<Icon>shopping_cart</Icon>
</Cart>);
}
}
Here your CartButton is not updating even though count is working fine because CartButton is not listening to your cookies variable. React component updates only when there is either props or state change.
You can something like this..
class CartButton extends React.Component {
state = {quantity: cookies.length}
componentDidMount(){
setInterval(function count() {
cookies = document.cookie.split("?");
this.setState({quantity: cookies})
}.bind(this), 10)
}
render() {
return (
<Cart onClick={e=>{show_cart()}}>
<Mfont>{this.state.quantity}</Mfont>
<Icon>shopping_cart</Icon>
</Cart>);
}
}

Why componentDidMount does not get fired?

I have a function which fires when the user clicks on it. The function calls setState, and the component rerenders itself. The only problem is that componentDidMount() does not get fired. Here's the code:
//the element which fires removeElem
<div onClick={this.removeElem}>
-
</div>
//the removeElem function gets called
removeElem = () => {
this.setState({status: 'deleted'})
}
//the component renders, the state has been changed
render(){
console.log(this.state.status) //the right value is logged after removeElem()
return(
//...
//componentDidMount
componentDidMount(){console.log("mounted")} //not logging
Why?
The componentDidMount() method just get trigerred when your component is mounted. You should use componentDidUpdate() method, that gets triggered when you update the state/props of your component.
You should read the React Docs for State and Lifecycle
See the snippet below:
class App extends React.Component {
state = { status: 'active' }
//componentDidMount
componentDidMount(){console.log("mounted")}
//componentDidUpdate
componentDidUpdate(){console.log("new status:",this.state.status)}
//the removeElem function gets called
removeElem = () => {
this.setState({status: 'deleted'})
}
//the component renders, the state has been changed
render(){
console.log(this.state.status) //the right value is logged
return( <button onClick={this.removeElem}>
Trigger it
</button>);
}
}
ReactDOM.render(<App/>,document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='root'></div>

React with lists in state, how to set new state?

I ran into an issue with updating part of the state that is a list that's passed on to children of a component.
I pass in a list to a child, but then have trouble to update that list and have the child reflect the new state;
<ItemsClass items={this.state.items1} />
When I change the value of this.state.items1, the component doesn't render with the new value.
this.setState({items1: []}); // this has no effect
However, if I change the already existing array (not replacing it new a new empty one), the component renders as I wish;
this.setState(state => { clearArray(state.items1); return state; });
That means the state updating function isn't pure, which React states it should be.
The HTML;
<div id='app'></div>
The js;
class ItemsClass extends React.Component {
constructor(props){
super(props);
this.state = {items: props.items};
}
render() {
var items = this.state.items.map(it => <div key={it.id}>{it.text}</div>);
return(
<div>{items}</div>
);
}
}
function ItemsFunction(props) {
var items = props.items.map(it => <div key={it.id}>{it.text}</div>);
return(
<div>{items}</div>
);
}
class App extends React.Component {
constructor(props){
super(props);
var items = [{id:1, text: 'item 1'}, {id: 2, text: 'item 2'}];
this.state = {
items1: items.slice(),
items2: items.slice(),
items3: items.slice()
};
this.clearLists = this.clearLists.bind(this);
}
clearLists() {
// for items1 and items2, clear the lists by assigning new empty arrays (pure).
this.setState({items1: [], items2: []});
// for items3, change the already existing array (non-pure).
this.setState(state => {
while (state.items3.length) {
state.items3.pop();
}
})
}
render() {
return (
<div>
<button onClick={this.clearLists}>Clear all lists</button>
<h2>Items rendered by class, set list to new empty array</h2>
<ItemsClass items={this.state.items1} />
<h2>Items rendered by class, empty the already existing array</h2>
<ItemsClass items={this.state.items3} />
<h2>Items rendered by function</h2>
<ItemsFunction items={this.state.items2} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
Try it out on codepen.
It seems that the ItemsClass doesn't update even though it's created with <ItemsClass items={this.state.items1}/> and this.state.items1 in the parent changes.
Is this the expected behavior? How can I update the state in the ItemsClass child from the parent?
I'm I missing something? This behavior seems quite error prone, since it's easy to assume that the child should follow the new state, the way it was passed in when the child was created.
You're copying the props of ItemsClass into the state when the component gets initialized - you don't reset the state when the props change, so your component's updates don't get displayed. To quote the docs:
Beware of this pattern, as state won't be up-to-date with any props update. Instead of syncing props to state, you often want to lift the state up.
If your component has to do something when the props change, you can use the componentWillReceieveProps lifecycle hook to do so (note that it doesn't get run when the component initially mounts, only on subsequent prop updates).
That said, there's zero reason for you to be duplicating the props here (and honestly there's rarely a good reason to do so in general) - just use the props directly, as you're doing with ItemsFunction, and everything will stay in sync:
class ItemsClass extends React.Component {
render() {
var items = this.props.items.map(it => <div key={it.id}>{it.text}</div>);
return(
<div>{items}</div>
);
}
}
Here's a working version of your Codepen: http://codepen.io/anon/pen/JNzBPV

Is there a synchronous alternative of setState() in Reactjs

According to the explaination in the docs:
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
So since setState() is asyncronous and there is no guarantee about its synchronous performance. Is there an alternative of setState() that is syncronous.
For example
//initial value of cnt:0
this.setState({cnt:this.state.cnt+1})
alert(this.state.cnt); //alert value:0
Since the alert value is previous value so what is the alternative that will give alert value:1 using setState().
There are few questions on Stackoverflow which is similar to this question but no where I am able to find the correct answer.
As you have read from the documentation, there is NO sync alternative, reason as described is performance gains.
However I presume you want to perform an action after you have changed your state, you can achieve this via:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
x: 1
};
console.log('initial state', this.state);
}
updateState = () => {
console.log('changing state');
this.setState({
x: 2
},() => { console.log('new state', this.state); })
}
render() {
return (
<div>
<button onClick={this.updateState}>Change state</button>
</div>
);
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
You could wrap setState in a function returning a promise, and then use this function with the await keyword to cause your code to wait until the state has been applied.
Personally, I would never do this in real code, instead I would just put the code I wish to execute after the state update in the setState callback.
Nevertheless, here is an example.
class MyComponent extends React.Component {
function setStateSynchronous(stateUpdate) {
return new Promise(resolve => {
this.setState(stateUpdate, () => resolve());
});
}
async function foo() {
// state.count has value of 0
await setStateSynchronous(state => ({count: state.count+1}));
// execution will only resume here once state has been applied
console.log(this.state.count); // output will be 1
}
}
In the foo function, the await keyword causes the code execution to pause until the promise returned by setStateSynchronous has been resolved, which only happens once the callback passed to setState is called, which only happens when the state has been applied. So execution only reaches the console.log call once the state update has been applied.
docs for async/await:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
If this is required I would suggest using a callback in your setState function (and I also suggest using a functional setState).
The callback will be called after the state has been updated.
For example, your example would be
//initial value of cnt:0
this.setState(
(state) => ({cnt: state.cnt+1}),
() => { alert(this.state.cnt)}
)
as per documentation here : https://facebook.github.io/react/docs/react-component.html#setstate
Note: Official docs say, "Generally we recommend using componentDidUpdate() for such logic instead."
No, there is not. React will update the state when it sees fit, doing things such as batching setState calls together for efficiency. It may interest you that you are able to pass a function into setState instead, which takes the previous state, so you may choose your new state with good knowledge of the previous one.
You can use flushSync from ReactDOM to update the state synchronously as suggested by react docs.
https://reactjs.org/docs/react-component.html
It may sound weird but yes setState can work synchronously in react.
How so? This is POC which I've created to demonstrate it.
Pasting the only app JS code.
Maybe it's possible that I'm missing something but this was actually happening in my application that's when I came to know about this effect.
Correct me if this kind of behavior is expected in React which I'm unaware of.
When there are multiple setState on main thread the setState runs a Batch combining all the setState on the main method. Whereas the Scenario is different when the same things go inside the async Function.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
this.asyncMethod = this.asyncMethod.bind(this);
this.syncMethod = this.syncMethod.bind(this);
}
asyncMethod() {
console.log("*************************")
console.log("This is a async Method ..!!")
this.setState({
counter: this.state.counter + 1
}, () => {
console.log("This is a async Method callback of setState. value of counter is---", this.state.counter);
})
console.log("This is a async Method on main thread. value of counter is---", this.state.counter);
console.log("*************************")
}
syncMethod() {
var that = this;
console.log("*************************")
console.log("This is a sync Method ..!!")
that.setState({counter: "This value will never be seen or printed and render will not be called"});
that.setState({counter: "This is the value which will be seen in render and render will be called"});
setTimeout(() => {
that.setState({counter: "This is part is synchronous. Inside the async function after this render will be called"});
console.log("setTimeout setState");
that.setState({counter: "This is part is aslso synchronous. Inside the async function after this render will be called"});
}, 10)
console.log("This is a sync Method on Main thread. value of counter is---", this.state.counter);
console.log("*************************")
}
render() {
console.log("Render..!!",this.state.counter);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
</header>
<button onClick={this.asyncMethod}>AsyncMethod</button>
<button onClick={this.syncMethod}>SyncMethod</button>
</div>
);
}
}
export default App;
Use React Hooks instead:
function MyComponent() {
const [cnt, setCnt] = useState(0)
const updateState = () => {
setCnt(cnt + 1)
}
useEffect(() => {
console.log('new state', cnt)
}, [cnt])
return (
<div>
<button onClick={updateState}>Change state</button>
</div>
)
}
I was able to trick React into calling setState synchronously by wrapping my code in setTimeout(() => {......this.setState({ ... });....}, 0);. Since setTimeout puts stuff at the end of the JavaScript event queue, I think React detects the setState is within it and knows it can't rely on a batched setState call (which would get added to the end of the queue).
In functional components I do this:
const handleUpdateCountry(newCountry) {
setIsFetching(() => true);
setCompanyLocation(() => newCountry);
setIsFetching(() => false);
}
Correct me if I'm wrong but as far as I know this is synchronous then and it also just worked in my situation.
Yes, there is a method with which we can make our synchronous setState. But its performance maybe not good as normally
For example, we have
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: 0
};
}
changeState(){
console.log('in change state',this.state.data);
this.state.data = 'new value here'
this.setState({});
console.log('in change state after state change',this.state.data);
}
render() {
return (
<div>
<p>{this.state.data}</p>
<a onClick={this.changeState}>Change state</a>
</div>
);
}
}
In this example, we change the state first and then render our component.
Short answer to your question is - NO, react doesn't have sync method setState.
In some cases, an alternative is using refs (createRef or useRef) instead of states.
//initial value of cnt:0
const cnt = React.createRef(0); //or React.useRef(0);
cnt.current++;
alert(cnt.current); //output: 1
So far the best solution for me is to use the callback function.
this.setState({cnt:this.state.cnt+1},() =>{
// other task which we want to run synchronously
alert(this.state.cnt);
})
according to the official documentation of react, we can make setState synchronous by passing the callback function in the second argument.

Categories

Resources