child component does not rerender with shouldComponentUpdate - reactjs

My child component changes its state when its prop is changed. I want to re-render child component ImageSlide so call different string when state changes. The className is well changed and console shows the changed values well. But it does not rerender view.
I tried shouldComponentUpdate, but it did not work.
How can I re-render ImageSlide?
let languages = {
en: require('textEnglish'),
kr: require('textKorean')
}
class ImageSlide extends Component {
constructor(props) {
super(props);
this.state={
lang: this.props.lang,
url: this.props.url
}
}
languageSelect=()=> {
if (this.state.lang === 'kr') {
return 'kr';
} else {
return 'en';
}
}
static getDerivedStateFromProps(nextProps, prevState){
if (nextProps.lang !== prevState.lang |
nextProps.url !== prevState.url )
{
return {lang : nextProps.lang, url: nextProps.url} ;
}
}
shouldComponentUpdate(nextProps, nextState){
return true;
//I tried also:
// if (nextProps.url !== this.state.url |
// nextProps.lang !== this.state.lang)
// return true;
}
render() {
const Text=languages[this.languageSelect()];
return (
<div className="test-transition">
{console.log(this.state.lang)}
{console.log(this.state.url)}
{console.log(Text["p1_3_"+String(this.state.url)])}
<div className={`pic${this.state.url}`}>
{Text["p1_3_"+String(this.state.url)]}
</div>
</div>
);
}
}

Check if the new props is changes or not using any of the lifecycle event like componentDidUpdate or shouldComponentUpdate or you can also try componentWillReceiveProps (not recommended since its deprecated)
componentDidUpdate()
{
/*Check for prev props condition*/
this.forceUpdate(); /*this method renders the component*/
}

Related

ReactJS: Prop referencing a state var. is not updated after parent's state change

I have a Top component which has a checkbox and also a child MyValidator component. When the checkbox changes, I have a handler in the Top component which sets its State var, this.state.activeItemLocked. The MyValidator child component specified in Top has a Prop with this state var.
However, when the Top Component changes its state, the MyValidator is not refreshed with this new change, even though it should render the current value which is its own state var received from Props:
Top
class Top extends React.Component {
constructor(props) {
super(props);
this.state = {
activeItemLocked: false;
};
}
render() {
return (
<div>
<input type="checkbox" defaultChecked={this.state.activeItemLocked} id="bChkboxLocked" onChange={this.handleCheckboxChange}></input>
<label htmlFor="bChkboxLocked">Locked</label>
<MyValidator value={this.state.activeItemLocked}></MyValidator>
<div>
);
}
// Handler to set State var. in Top upon Checkbox Toggle
handleCheckboxChange = (e) => {
this.setState({
activeItemLocked: e.target.checked ? true : false
});
};
}
MyValidator
class MyValidator extends Component {
constructor(props) {
super(props);
this.state = {
value: this.props.value // Initialize MyValidator's State var. from Props
};
}
render() {
return (
<div style={{"color":"red"}}>Checkbox current state: {this.state.value}</div>
);
}
}
Issue : Upon Top's state change, the Prop-based MyValidator state change does not occur (it does not display the current value).
Keeping a class component, there are many ways you can achieve this. 2 of them are:
Don't copy a props to a state variable. Instead just use the props.
class MyValidator extends Component {
render() {
return (
<div style={{"color":"red"}}>Checkbox current state: {this.props.value}. </div>
);
}
}
Use getDerivedStateFromProps
class MyValidator extends Component {
static getDerivedStateFromProps(props, state) {
if (props.value !== state.value) {
return {value: props.value}
}
return state;
}
render() {
return (
<div style={{"color":"red"}}>Checkbox current state: {this.state.value}</div>
);
}
}
Constructor only runs once. So you need to use getDerivedStateFromProps to update child state before render:
static getDerivedStateFromProps(props){
return {
value: props.value
};
}
also convert the boolean value to a string so that it gets printed out:
{this.state.lol.toString()}
Sandbox: https://codesandbox.io/s/late-https-3eh52?fontsize=14&hidenavigation=1&theme=dark

How to reset state in a component on prop change

How should I reset the state of a Child on every prop change?
Parent component:
render() {
const { show, month } = this.props; // January, February, ...
return (
{ show ? <Child selectedMonth={month} /> : null }
);
}
Child component:
componentDidMount() {
this.resetChildState();
// this will never run if the Parent's month prop is changed
// only if show becomes false from true
}
I want resetChildState to run on every month change.
Just try to use componentDidUpdate, in the body of this method you can compare whether props from parent changed or not. So if they did just call your reset method or whatever you want.
For more info visit https://reactjs.org/docs/react-component.html#componentdidupdate
componentDidUpdate(prevProps, prevState) {
if(this.props.selectedMonth!== prevProps.selectedMonth) {
this.resetChildState();
}
}
You can use getDerivedStateFromProps()
static getDerivedStateFromProps(nextProps, prevState) {
if(monthChanged){
return initialState; //reset to initialState that you have defined.
}
return null;
}
if you want just reset your state exactly after changing the props, you can use the componentWillReceiveProps react lifecycle as below:
class Children extends React.Component {
// ...
state = {
name: "test"
};
componentWillReceiveProps(nextProps) {
this.setState({name: ""});
}
// ...
}
use ComponentDidUpdate
componentDidUpdate() {
if (this.props.id !== this.state.section_id) {
this.setState({
section_id:this.props.id,
filteredProducts:[],
productsByPage:[],
pageNumber:0,
hasMore:true,
showloading:true
},()=>this.fetchData());
}
}

Passing value to props reactjs

I am trying pass value to my child components. The value that I am getting is coming from the an API that I called in my parent component and being called in the componentDidMount but the problem is the child components is not reading the props I am passing in his own componentDidMount, its only getting blank even in the reactdevtool it passing correct values. I solved this before but cannot remember what I did can you help. Thanks
Child:
componentDidMount() {
const {
events
} = this.props;
console.log(events)
}
Parent:
class App extends Component {
componentDidMount() {
let self = this;
GetAllMainItems().then(function(GetAllMainItemsResults) {
let MainObject = self.state.MainObject;
self.setState({
GetAllMainItemsResults
});
}
}
render() {
constructor() {
super();
this.state = {
MainObject: []
};
}
return ( <
div className = "App row" >
<
Calendar events = {
this.state.MainObject
}
/>
<
/div>
);
}
There are a few things you need to review.
constructor should be outside of render method.
You do not have to use let self = this. you can just do this.setState({...}) there.
Look at your GetAllMainItems callback. I don't know what you get
there. but you are definitely not setting mainObject in your state.
Instead, you will have this.state.GetAllMainItemsResults.
Recommendations
Try to understand object destructuring.
Use arrow functions
Hope it helps.
Parent Component
```
class App extends Component {
state = {
mainObject: ""
};
componentDidMount() {
GetAllMainItems().then(response => {
this.setState({
mainObject: response
});
});
}
render() {
const { mainObject } = this.state;
return (
<div className="App row">
<Calendar events={mainObject} />
</div>
);
}
}
The problem you are having is that your child component is re-rendering when it receives new events props.
Try adding a componentDidUpdate method to see these props updating:
componentDidUpdate(prevProps, prevState) {
console.log(prevProps, prevState);
console.log('events:', prevProps.events, this.props.events);
}

How to update Highchart from inside react a component?

I am working with react 16.3 where componentWillUpdate is deprecated (strict mode). We have a react wrapper around Highcharts and used to update the highchart in componentWillUpdate that runs just before render.
But now in react 16.3 when the input highchartoptions prop updates, there seems to be no way to call Highchart.update before render() is called. Its suggested to use componentDidUpdate but its called only after render() and it doesn't seem to work at all.Any suggestions will help.
Code snippet here:
export class HighchartReactWrapper extends React.Component {
constructor(props) {
super(props);
// We maintain the user provided options being used by highchart as state
// inorder to check if chart update is needed.
this.state = { highChartOptions: this.props.options };
this.onChartRendered = this.onChartRendered.bind(this);
}
componentDidMount() {
// Create chart
this.chart = new Highcharts.Chart(this.container, this.state.highChartOptions, this.onChartRendered);
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.options !== prevState.options) {
return { highChartOptions: nextProps.options };
}
}
componentDidUpdate() {
this.chart.update(this.state.highChartOptions, false, true); <---- Doesn't work
}
onChartRendered() {
// Callbacks..
if (this.props.onChartRenderedCallback !== undefined) {
this.props.onChartRenderedCallback();
}
}
componentWillUnmount() {
// Destroy chart
this.chart.destroy()
}
render() {
return (
<div className="react-highchart-wrapper">
<div id={container => this.container = container} />
</div>
);
}
}
HighchartReactWrapper.propTypes = {
/**
* Chart options to be used in Highcharts library.
*/
options: PropTypes.object.isRequired,
onChartRenderedCallback: PropTypes.func
};
HighchartReactWrapper.defaultProps = {
options: undefined,
onChartRenderedCallback: undefined
};
You may use shouldComponentUpdate(nextProps, nextState) which is called before the component rerender.

How can I delay rendering until then() of promise completes?

I'm using reactjs with redux and this is a part of one of my components.
I want to render postList of the promise which is returned by handleFetchPosts, but the render() function starts rendering before the then() of shouldComponentUpdate is completed.
How can I postpone rendering or rerender after the then is completed?
constructor(props) {
super(props);
this.postList = props.postList;
this.props.handleFetchPosts('/main', 'about', '');
}
shouldComponentUpdate(nextProps) {
if (this.postList !== nextProps.postList) {
nextProps.postList.then(data => {
this.postList = data;
});
return true;
}
else {
return false;
}
}
render() {
return (
<div className="content">
<p>
{this.postList + ""}
{/*content*/}
</p>
</div>
);
}
Apologies, I've totally changed my answer, I bypassed the fact you are using Redux.
The fact that you're using Redux, I'd set up an async action that shouldComponentUpdate calls, which kick-starts the Promise call.
This way you can update a flag in your state after starting the Promise to use in the render method to prevent rendering.
Once the call has finished send the final ACTION with the results and change the flag back.
For manage your component rendering use the state of your component. and update it when you receive your data.
constructor(props) {
super(props);
this.state={
postList:this.props.postList;
}
this.props.handleFetchPosts('/main', 'about', '');
}
shouldComponentUpdate(nextProps) {
if (this.postList !== nextProps.postList) {
nextProps.postList.then(data => {
this.setState({
postList:data
});
});
return true;
}
else {
return false;
}
}
render() {
if(!this.state.postList){
return <div>Loading</div>
}
return (
<div className="content">
<p>
{this.state.postList + ""}
{/*content*/}
</p>
</div>
);
}

Resources