How to replace componentDidMount with getderivedstatefromprops? - javascript

I want to use getderivedstatefromprops to update the state of selectedPlan when it receives a different number from the prop defaultPlan.
I have tried to convert componentDidMount to getderivedstatefromprops, but getderivedstatefromprops does not seem to be getting called in my component?
state = {
selectedPlan: 0,
};
static getderivedstatefromprops(props, state){
if (props.defaultPlan !== state.selectedPlan) {
return{
selectedPlan: props.defaultPlan
};
}
return null;
}
componentDidMount() {
if (this.props.plans.length > this.props.defaultPlan) {
this.setState({ selectedPlan: this.props.defaultPlan });
}
}
I am using this in a PureComponent, and I'm not sure if thats the reason why it is not working?

Related

React.JS - ComponentDidUpdate Method Does Gives Infinite Loop for Axios Calls

Good day,
I'm trying to fetch a new data once a new props has changed on my route.
Here's my sample routing:
<Route path={'/students/:selectedPage'} exact component={Students} />
Basically, I have a method that fetches my data. So I'm invoking it in my componentDidMount() Here are my sample codes:
componentDidMount(): void {
this.getData()
}
getData(selectedPage){
axios.get(`http://localhost:1343/students/${selectedPage}`).then(response=>{
this.setState({
students: response.data
})
}).catch(error=>console.log(error)
}
For my componentDidUpdate method which trigger if there's new changes in the param of my url.
componentDidUpdate(): void {
if (this.props.match.params.selectedPage !== prevProps.selectedPage){
this.getData(this.props.match.params.selectedPage)
}
}
Unfortunately, it causes to have infinite request from the web api. Which makes the table laggy. I tried to create a state with a name, hasLoaded but it gives me an error message that says, infinite loop has detected.
Any advice?
Compare your Props in ComponentWillReceiveProps like this,
shouldComponentUpdate: function(nextProps, nextState) {
return nextProps.selectedPage != this.props.selectedPage ;
}
componentWillReceiveProps(nextProps) {
if (nextProps.selectedPage !== this.props.selectedPage ) {
//this.setState({ selectedPage : nextProps.selectedPage })
this.getData(nextProps.selectedPage)
}
}
or Do this instead Keep your selectedPage in state
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.selectedPage !== prevState.selectedPage ) {
return { selectedPage : nextProps.selectedPage };
}
else return null;
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.selectedPage !== this.props.selectedPage ) {
//Perform some operation here
//this.setState({ selectedPage : this.props.selectedPage });
this.getData(this.props.selectedPage)
}
}
```

set multiple states, and push to state of array in one onClick function

I'm running into a recurring issue in my code where I want to grab multiple pieces of data from a component to set as states, and push those into an array which is having its own state updated. The way I am doing it currently isn't working and I think it's because I do not understand the order of the way things happen in js and react.
Here's an example of something I'm doing that doesn't work: jsfiddle here or code below.
import React, {Component} from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
categoryTitle: null,
categorySubtitle: null,
categoryArray: [],
}
}
pushToCategoryArray = () => {
this.state.categoryArray.push({
'categoryTitle': this.state.categoryTitle,
'categorySubtitle': this.state.categorySubtitle,
})
}
setCategoryStates = (categoryTitle, categorySubtitle) => {
this.setState({
categoryTitle: categoryTitle,
categorySubtitle: categorySubtitle,
})
this.pushToCategoryArray();
}
render() {
return (
<CategoryComponent
setCategoryStates={this.setCategoryStates}
categoryTitle={'Category Title Text'}
categorySubtitle={'Category Subtitle Text'}
/>
);
}
}
class CategoryComponent extends Component {
render() {
var categoryTitle = this.props.categoryTitle;
var categorySubtitle = this.props.categorySubtitle;
return (
<div onClick={() => (this.props.setCategoryStates(
categoryTitle,
categorySubtitle,
))}
>
<h1>{categoryTitle}</h1>
<h2>{categorySubtitle}</h2>
</div>
);
}
}
I can see in the console that I am grabbing the categoryTitle and categorySubtitle that I want, but they get pushed as null into this.state.categoryArray. Is this a scenario where I need to be using promises? Taking another approach?
This occurs because setState is asynchronous (https://reactjs.org/docs/state-and-lifecycle.html#using-state-correctly).
Here's the problem
//State has categoryTitle as null and categorySubtitle as null.
this.state = {
categoryTitle: null,
categorySubtitle: null,
categoryArray: [],
}
//This gets the correct values in the parameters
setCategoryStates = (categoryTitle, categorySubtitle) => {
//This is correct, you're setting state BUT this is not sync
this.setState({
categoryTitle: categoryTitle,
categorySubtitle: categorySubtitle,
})
this.pushToCategoryArray();
}
//This method is using the state, which as can be seen from the constructor is null and hence you're pushing null into your array.
pushToCategoryArray = () => {
this.state.categoryArray.push({
'categoryTitle': this.state.categoryTitle,
'categorySubtitle': this.state.categorySubtitle,
})
}
Solution to your problem: pass callback to setState
setCategoryStates = (categoryTitle, categorySubtitle) => {
//This is correct, you're setting state BUT this is not sync
this.setState({
categoryTitle: categoryTitle,
categorySubtitle: categorySubtitle,
}, () => {
/*
Add state to the array
This callback will be called once the async state update has succeeded
So accessing state in this variable will be correct.
*/
this.pushToCategoryArray()
})
}
and change
pushToCategoryArray = () => {
//You don't need state, you can simply make these regular JavaScript variables
this.categoryArray.push({
'categoryTitle': this.state.categoryTitle,
'categorySubtitle': this.state.categorySubtitle,
})
}
I think React doesn't re-render because of the pushToCategoryArray that directly change state. Need to assign new array in this.setState function.
// this.state.categoryArray.push({...})
const prevCategoryArray = this.state.categoryArray
this.setState({
categoryArray: [ newObject, ...prevCategoryArray],
)}

How to access and smoothly transition methods and this.state in componentWillReceiveProps to getDerivedStateFromProps?

componentWillReceiveProps(nextProps) {
if (
**this.state.clicked** &&
this.props.something &&
nextProps.addSubtract
) {
this.setState({ valid: nextProps.isValid });
}
if (!this.props.someItems) {
this.setState({ products: nextProps.propsValues});
}
if (nextProps.valueOfProps && **this.methodOnClass**) {
..........
..........
}
}
The code above is similar to a new project I'm working on and I have encountered a road-block. Since componentWillReceiveProps is deprecated and I need to replace a lot of code inside it with this.method and this.state , is there a way to access these in getDerivedStateFromProps without moving a lot of logic in other lifecycle methods.
First of all getDerivedStateFromProps is not a replacement of componentWillReceiveProps. See this post for more detail.
To return the state by using getDerivedStateFromProps, you simply return an object.
Example:
return {
myState: 'updated state'
}
If no condition match, simply return null:
return null
Similar to setState example:
setState({myState: 'updated state'})

Accessing class function inside of static getDerivatedPropsFromState

I'm refactoring the deprecated life-cycle method componentWillRecieveProps() with its new successor static getDerivatedPropsFromState()
The problem that I'm facing with this refactor is that componentWillRecieveProps() use a class function inside:
componentWillRecieveProps(nextProps) {
if(this.props.theme !== nextProps.theme) {
this.changeTheme()
}
}
So when I try to access this function inside of the new life-cycle method:
static getDerivatedPropsFromState(nextProps, prevState) {
if(prevState.theme !== nextProps.theme) {
this.changeTheme();
return { prevState: nextProps.theme };
}
return null;
}
An error jump saying this.changeTheme() is undefined.
How should I make reference to this function inside of static getDerivatedPropsFromState()?
Any help and explanation on why this is happening will be really appreciated.
getDerivatedPropsFromState is used for only updating the state. You should use componentDidUpdate in place of componentWillReceiveProps if you are planning to do updates.
See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#fetching-external-data-when-props-change
In the blog it mentions:
The recommended upgrade path for this component is to move data updates into componentDidUpdate. You can also use the new getDerivedStateFromProps lifecycle to clear stale data before rendering the new props.
So instead of
componentWillRecieveProps(nextProps){
if(this.props.theme !== nextProps.theme){
this.changeTheme()
}
}
One alternative is to do this
static getDerivatedPropsFromState(nextProps, prevState) {
if(prevState.theme !== nextProps.theme) {
return { theme: nextProps.theme };
}
return null;
}
componentDidUpdate(prevProps, prevState) {
if (this.state.theme !== prevState.theme) {
this.changeTheme();
}
}

React render method that depends on asynchronous request and state change

I am trying to learn ReactJS and Redux, and have come across a problem that I cannot seem to get over.
I have a React component, that gets data from an asynchronous request.
export class MyPage extends React.Component {
constructor(props) {
super(props)
this.state = {
enableFeature: false,
}
this.handleEnableFeatureChange = this.handleEnableFeatureChange.bind(this)
}
componentWillMount () {
this.fetchData()
}
fetchData () {
let token = this.props.token
this.props.actions.fetchData(token)
}
handleEnableFeatureChange (event) {
this.setState({ enableFeature: event.target.checked })
}
render () {
if (this.props.isFetching) {
return (
<div>Loading...</div>
)
} else {
return (
<div>
<label>Enable Feature
<input type="checkbox"
className="form-control"
checked={this.props.enableFeature}
onChange={this.handleEnableFeatureChange}
/>
</label>
</div>
)
}
}
}
So, my problem now is that, when I change the state of the checkbox, I want to update the state of my data. However, every time I update the state of my data, the react component method shouldComponentUpdate kicks in, and uses the current props to render the original data.
I would like to see how such cases are handled in general.
Thanks.
Try to do it like the following, i.e.
Use componentWillReceiveProps to assign props.enableFeature to state.enableFeature. From documentation
componentWillReceiveProps() is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
componentWillReceiveProps() is not invoked if you just call this.setState()
Use this state to load the value of checkbox
Manipulate this state (onchange) to update the value of checkbox
Following code can work in your case
export class MyPage extends React.Component {
static propTypes = {
isFetching: React.PropTypes.bool,
enableFeature: React.PropTypes.bool,
token: React.PropTypes.string,
actions: React.PropTypes.shape({
fetchData: React.PropTypes.func
})
};
state = {
enableFeature: false,
};
componentWillMount () {
this.fetchData();
}
/* Assign received prop to state, so that this state can be used in render */
componentWillReceiveProps(nextProps) {
if (this.props.isFetching && !nextProps.isFetching) {
this.state.enableFeature = nextProps.enableFeature;
}
}
fetchData () {
const { token } = this.props;
this.props.actions.fetchData(token)
}
handleEnableFeatureChange = (event) => {
this.setState({ enableFeature: event.target.checked })
};
render () {
return (<div>
{ this.props.isFetching && "Loading..." }
{
!this.props.isFetching && <label>
Enable Feature
<input
type="checkbox"
className="form-control"
checked={this.state.enableFeature}
onChange={this.handleEnableFeatureChange}
/>
</label>
}
</div>);
}
}
Note: The above code was not executed, but should work (babel's stage-0 code)
Change it to checked={this.state.enableFeature}

Categories

Resources