Render methods issue - React js - reactjs

Here I'm trying to get value from DefaultOpts.jsx and update the values to setState in Filters.jsx. But I'm getting error as below :
setState(...): Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.
Filters.jsx
import React from 'react';
import DefaultOpts from 'DefaultOpts.jsx';
export default class Filters extends React.Component {
constructor(props) {
super(props);
this.state = {
vOptions : []
}
this.handleOptions = this.handleOptions.bind(this)
}
handleOptions(params) {
console.log(params)
this.setState({
vOptions : params
});
}
componentDidMount() {
}
componentDidUpdate() {
}
render() {
return (
<div>
<DefaultOpts handleOptions={this.handleOptions.bind(this)} />
</div>
)
}
}
DefaultOpts.jsx
import React from 'react';
class DefaultOpts extends React.Component{
constructor(props) {
super(props);
}
componentDidMount() {
}
componentDidUpdate() {
}
render() {
var optArray = "";
$.ajax({
type: "get",
url: "url-path",
success: function(data) {
optArray = data;
}
});
return (
<div>
{this.props.handleOptions(optArray)}
</div>
)
}
}
export default DefaultOpts;
I got some answers in stackoverflow but I'm not able to get what's issue in my code. Please suggest me here what's wrong in my code..

You can't call this.props.handleOptions inside the render because it will trigger setState of the parent component - and you are still inside the rendering process. That's why it complains.
Try to execute this function inside the componentDidMount (together with your ajax call)

There are several problems with your code:
1) First and main one that results in the mentioned error is the fact that by calling handleOptions in render you are calling setState that in turn starts react life cycle. This is a really bad practice and always should/can be avoided.
2) You have one more async call to $.ajax in render that does not directly result in updating state but still considered a bad practice.
To conclude - your render function must not result in any app logic being performed, its task is to render results that have already been prepared. Do all heavy/async work in componentDidMount/componentDidUpdate and you will be fine.

render will execute before didMount... so you are setting the state before it is mounted
anyway move the $.ajax call to didMount, you shouldn't be doing logic things in render()

Related

ReactJs: Intial Component Rendering doesn't seems to work

I'm working on a project and came across this issue that first time when component is rendered, it doesn't shows anything on the screen. Then componentDidMount is invoked and after that something is visible on the screen.
My App Component
class App extends React.Component {
state={loading: true};
componentDidMount(){
alert("Inside did mount");
this.setState({loading: false});
}
render() {
alert(this.state.loading);
if (this.state.loading) {
return <div>Loading...</div>;
}
return(
<div>After ComponentDid Mount !</div>
)
}
}
In above code, initially Loading... should be visible on the screen for the first time. But this isn't happening.
Am I doing some kind of mistake here ?
Is there any way to show Loading... on the screen first and after that componentDidMount will run ?
your state for a class component needs to be inside a constructor.
constructor(props) {
super(props)
this.state = {your state}
}
Now that will allow you reference it as the component using (this)
It’s currently a normal variable and isn’t used as this.state but just state but that means changes to this variable may not reflect to changes to the pages. Further reading on this:
https://reactjs.org/docs/hooks-state.html
Also you may want to begin using functional components the way you wrote your render()
In my experience It’s preferable to have a single return in render and then from that return call upon functions and variables to render the page differently
It mounts the component fast so you can't see the loading text. You can use the setTimeout function to mimic remote server requests and latency.
import React from "react";
class App extends React.Component {
state = { loading: true };
componentDidMount() {
setTimeout(() => {
this.setState({ loading: false });
}, 5000);
}
render() {
if (this.state.loading) {
return <div>Loading...</div>;
}
return <div>After ComponentDid Mount !</div>;
}
}
export default App;

will puting function inside render method slow my app

i am learning react, i am django developer and i am getting data from api and is working fine, just for a fun i wanted to update data without clicking reload, so what i did is put fetch function inside render which is looping , and it is giving real time feel, but i want to know that will doing this like this slow my app like in vannila js, since it is rendering again and again, but since react uses virtual dom i have soft side on this technique
import React from 'react';
class FetchRandomUser extends React.Component {
constructor(){
super();
this.state = {
data: [],
};
}
fetchdata(){
fetch('http://127.0.0.1:8000/api')
.then(response=>response.json())
.then((data)=>{
this.setState({
data:data,
});
console.log(data);
});
}
componentDidMount(){
this.fetchdata();
}
render() {
this.fetchdata();
const dat = this.state.data;
const rows = dat.map((emp)=>
<div>
<h1>{emp.id}</h1>
<h1>{emp.title}</h1>
<h1>{emp.body}</h1>
</div>
);
return (
<div>
{rows}
</div>
)
}
}
export default FetchRandomUser;
You shouldn't use fetch inside render method. Because in your code when this.fetchData() is run, as you can see you are setting the state in that method using setState(), and setState itself calls render method so your code will result in an infinite loop.
Also You shouldn't use fetch inside render method because every time render is called your fetchData() will run which will slow your program as it takes time to fetch data from a server.

ReactJS lifecycle setState in componentDidMount

I have 2 components for demonstration of my problem:
Parent:
import React from "react";
import ReactDOM from "react-dom";
import { Grid, Row } from "react-flexbox-grid";
import Hello from "./Hello";
class App extends React.Component {
state = {
name: "Michal"
};
componentDidMount = () => {
this.setState({ name: "Tina" });
};
componentDidUpdate(prevState) {
console.log("App componentDidUpdate", prevState, this.state);
}
handleUpdate = value => {
console.log("App handleUpdate");
this.setState({ name: value });
};
render() {
return (
<Grid>
<Row>
<Hello name={this.state.name} update={this.handleUpdate} />
</Row>
</Grid>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
Child:
import * as React from "react";
class Hello extends React.PureComponent {
componentDidMount() {
// setTimeout(() => {
this.props.update("Matus");
// }, 0);
}
componentDidUpdate(prevProps) {
console.log("Hello componentDidUpdate", prevProps, this.props);
}
render() {
return <h1>Hello {this.props.name}!</h1>;
}
}
export default Hello;
In child component I want to set value in parent state via props function. But setState function is ignored, it works if props function is called from setTimeout.
Can you explain me why it work in setTimeout, why I should avoid this construction. And what is correct way to do it?
Hello component represent "Select", which in componentDidMount will fetch options and set default value.
Thank you.
Components initialise from the bottom up in React. So in your example Hello triggers componentDidMount, attempts to set the state in App via this.props.update, then App overrides it a split-second later when it calls its own componentDidMount. The name you set in the child component never reaches the state.
I'm not sure what the purpose of this is, hopefully only for leaning purposes as components shouldn't need to immediately set their own state when mounting. If you need to perform some logic before initialising the state in App you can use a constructor and do it there.
Regardless, the solution is remove the initial state setter in App.
It is not ignored and it does fire. You are just not observing it with your logs.
Check out:
https://codesandbox.io/s/kind-jackson-b2r2b?file=/src/App.js
In the console you will see the following execution order in the console window:
Hello componentDidMount props = Object {name: "Michal", update: function ()}
App handleUpdate value = Matus
App componentDidMount props = Object {}
Hello componentDidUpdate props = Object {name: "Tina", update: function ()}
App componentDidUpdate state = Object {}
Object {name: "Tina"}
Thus you will see the child componentDidMount fires and completes mount before the parent component completed and fires its componentDidMount, as components completes mounting from the child components up.
So you just never observe the state going to Matus because it triggers a new state change to Tina when it completes mounting.
You setState function from Hello component is ignored because of the React lifecycle. Basically App componentDidMount function overrides your state change from Hello component before it was rendered. That's why setTimeout helps, it moves your state change to the new rendering loop.
I don't know exact why you are trying to load data and pass it from the child component to parent but the good practice in React is to pass data from top to bottom. So the better solution would be to use Select component to just render the data from parent and react to user events.
<Select options={options} selected={option} handle={handleSelect} />
Reason:
React rendering is synchronous.
Rendering is a depth-first traversal
Now,
componentDidMount() {
this.props.update("Matus");
}
Is executed first, which sets the name Matus. Then the following executes -
componentDidMount = () => { this.setState({ name: "Tina" }); };
This sets the name Tina.
All of this happens on the first call-stack where the rendering happens. If we use setTimeout(), then
this.props.update("Matus");
will be moved to the second call-stack, which will be executed after the initial rendering and mounting has ended, thus setting the name Tina and triggering a re-render.
If you want to use class components, you need to use a constructor function to initialise state and pass the props from parent to child.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "Michal"
};
}
// ... rest of parent component
import * as React from "react";
class Hello extends React.PureComponent {
constructor(props) {
super(props)
}
componentDidMount() {
// setTimeout(() => {
this.props.update("Matus");
// }, 0);
}
componentDidUpdate(prevProps) {
console.log("Hello componentDidUpdate", prevProps, this.props);
}
render() {
return <h1>Hello {this.props.name}!</h1>;
}
}
export default Hello;

React / Redux Components not re-rendering on state change

I think this question has been answer several time but I can't find my specific case.
https://codesandbox.io/s/jjy9l3003
So basically I have an App component that trigger an action that change a state call "isSmall" to true if the screen is resized and less than 500px (and false if it is higher)
class App extends React.Component {
...
resizeHandeler(e) {
const { window, dispatch } = this.props;
if (window.innerWidth < 500 && !this.state.isSmall) {
dispatch(isSmallAction(true));
this.setState({ isSmall: true });
} else if (window.innerWidth >= 500 && this.state.isSmall) {
dispatch(isSmallAction(false));
console.log(isSmallAction(false));
this.setState({ isSmall: false })
}
};
componentDidMount() {
const { window } = this.props;
window.addEventListener('resize', this.resizeHandeler.bind(this));
}
...
I have an other component called HeaderContainer who is a child of App and connected to the Store and the state "isSmall", I want this component to rerender when the "isSmall" change state... but it is not
class Header extends React.Component {
constructor(props) {
super(props);
this.isSmall = props.isSmall;
this.isHome = props.isHome;
}
...
render() {
return (
<div>
{
this.isSmall
?
(<div>Is small</div>)
:
(<div>is BIG</div>)
}
</div>
);
}
...
even if I can see through the console that redux is actually updating the store the Header component is not re-rendering.
Can someone point out what I am missing ?
Am I misunderstanding the "connect()" redux-react function ?
Looking at your code on the link you posted your component is connected to the redux store via connect
const mapStateToProps = (state, ownProps) => {
return {
isHome: ownProps.isHome,
isSmall: state.get('isSmall')
}
}
export const HeaderContainer = connect(mapStateToProps)(Header);
That means that the props you are accessing in your mapStateToProps function (isHome and isSmall) are taken from the redux store and passed as props into your components.
To have React re-render your component you have to use 'this.props' inside the render function (as render is called every time a prop change):
render() {
return (
<div>
{
this.props.isSmall
?
(<div>Is small</div>)
:
(<div>is BIG</div>)
}
</div>
);
}
You are doing it well in the constructor but the constructor is only called once before the component is mounted. You should have a look at react lifecycle methods: https://reactjs.org/docs/react-component.html#constructor
You could remove entirely the constructor in your Header.js file.
You should also avoid using public class properties (e.g. this.isSmall = props.isSmall; ) in react when possible and make use of the React local state when your component needs it: https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class
A component is only mounted once and then only being updated by getting passed new props. You constructor is therefore only being called once before mount. That means that the instance properties you set there will never change during the lifetime of your mounted component. You have to directly Access this.props in your render() function to make updating work. You can remove the constructor as he doesn't do anything useful in this case.

State is not updating in componentWillMount

When i am trying to update the state of react component from an api call in componentwillmount function it is not working as expected. The value is not getting set.
export default class ListOfProducts extends Component {
componentWillMount() {
console.log('component currently mounting');
fetchOrder().then(data => {
console.log('order api call has been finished.', data);
this.setState(data, function() {
//this should print new data but i am still getting old data
console.log('current this.state.', this.state.orders)
})
})
}
constructor(props) {
super(props);
this.state = {
"orders": {
"order_items": []
}
};
}
render() {
let productList = [];
let productUnit = (
<View>
{this.state.orders.order_items.map(function(order,i){
return <ProductListItem
delivered={true}
productSKU={3}/>
})}
</View>
);
return productUnit;
}
}
If you want to perform any asynchronous requests, I suggest you perform them in the componentDidMount lifecycle method. This is the suggested path based on the Reactjs documentation for componentDidMount.
Invoked once, only on the client (not on the server), immediately
after the initial rendering occurs. At this point in the lifecycle,
you can access any refs to your children (e.g., to access the
underlying DOM representation). The componentDidMount() method of
child components is invoked before that of parent components.
If you want to integrate with other JavaScript frameworks, set timers
using setTimeout or setInterval, or send AJAX requests, perform those
operations in this method.
Perhaps you need to change:
" extends Component { "
to
" extends React.Component { "

Resources