React child component state not updating when new props are passed - javascript

I'm rendering a custom modal component that is displayed based on props passed in from a parent component. The prop isVisible is initially false and then updated in the parent component via a button. I'm checking the component state via the console.log statement in the render function. When the component is first initialized, it logs false false as expected but when the isVisible is updated, it returns false true. Why is the state not updating with the props?
class MyModal extends React.Component {
constructor(props) {
super(props);
this.state = {
createModalVisible:props.isVisible,
};
setCreateModalVisible = (visible) => {
this.setState({createModalVisible:visible});
}
componentWillMount(){
this.setCreateModalVisible(this.props.isVisible);
}
}
render() {
console.log(this.state.createModalVisible,this.props.isVisible);
return (//modal stuff)
}
}
export default MyModal
I get that this is probably pretty basic component lifecycle stuff but I couldn't figure it out from the docs and am fairly new to React.

Since the constructor and componentWillMount is only run once per component mounted in to the DOM tree the state wouldn't be updated on each props being passed down.
this.state is local for each component instance and needs to be updated with this.setState() to be able to use an updated state object on each render pass.
Use
componentWillReceiveProps in which you can set the state to reflect the new prop being passed in for you to get what you need.

Related

ReactJS - What is difference between component state and class variable?

I think it works differently but I don't know how it works.
1. Using class variable
export default class Test extends Component {
constructor() {
this.active = false;
}
render() {
this.active = this.props.name === 'Dan'? true : false;
return (
<div>
{this.active? 'ssup?' : 'noooo'}
</div>
);
}
}
2. Using React component state
export default class Test extends Component {
constructor() {
this.state = { active: false };
}
render() {
if(this.props.name === 'Dan') {
this.setState({active: true});
}
return (
<div>
{this.active? 'ssup?' : 'noooo'}
</div>
);
}
}
I think it doesn't need to re-render using State if it's only affected by received props.
The difference between the two is that React will re-render your component when state changes (with this.setState(/*...*/)).
If you update the class variable, React will be unaware of it and won't re-render your component.
Note that what you're achieving in your code requires neither state or class variable. You're simply computing another value directly from the props. A better way to write your component would be like this :
export default class Test extends Component {
render() {
const active = this.props.name === 'Dan';
return (
<div>
{active? 'ssup?' : 'noooo'}
</div>
);
}
}
The simple answer to your question is that by using state you call the setState() which automatically calls render() automatically. Which cannot be obtained by class variables
You use the `state variables` when you want to change the component when that variable is changed.
When you don't want to automatically call `render()` you use the `class` variables
React component only re-renders when there are changes to state or class. But updating class variable involves neither so it does not trigger render.
Though using state may seem similar to class variable but state is a protected keyword in React that refers to stored component data. The major difference between using class variables and state is updating data. Instead of manually reassigning the variable, you call this.setState().
When you call this.setState(). It compares this new state to the previous state. If there is a change, React re-renders the component, resulting in the updated count value displayed on the screen.
But when you update class variable, it sure gets updated but does no re-render. You can do so using this.forceUpdate(). But Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render().
Refer to this article for detailed info.

setting a key in a component does not re-render the component on state updates

I have a component that has two children:
A component where the user inputs and select a topic
A component that renders results based on the topic selected in the previous component
So this is the code of the parent component:
import SearchBox from "../../components/SearchBox";
import Results from "../../components/Results";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
selectedTopic: null,
};
}
onSelectTopic = (topic) => this.setState({
selectedTopic: topic,
});
render() {
return (
<div>
<SearchBox
onSelectTopic={this.onSelectTopic}
/>
<Results
key={this.state.selectedTopic && this.state.selectedTopic.id}
selectedTopic={this.state.selectedTopic}
/>
</div>
);
}
}
export default Home;
Basically, the SearchBox component updates the state of the parent component that propagates the selection to the Results component.
The Results component has its own state, that I want to clear when a new selection is passed. Instead of using getDerivedStateFromProps in Results to clear its state I have added a key to <Results key={...}/> as in the snippet above.
I was expecting that every time the state in the parent component updates (ie, the onSelectTopic method is called above), the Results component will be recreated, but instead the component does not update at all. Am I missing something? If I remove its key property, Results updates as expected (ie without clearing its internal state, which is what I want to achieve by using the key)
Btw, I'm using preact instead of react, but my understanding is that the behaviour regarding the key property on components is the same.

Getting a changed variable to show up in `render`

Probably a simple question with a simple answer, but I can't figure this out. Blame the heat. My simulator prints 'this is a test' on the screen, when I want it to say 'changed'. What am I doing wrong?
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Template from './src/components/Template';
export default class App extends React.Component {
constructor(props) {
super(props);
this.foo= "this is a test";
}
changeMe = () => {
this.foo = 'changed';
}
componentDidMount(){
this.changeMe();
}
render() {
return (
<Template foo={this.foo} />
);
}
}
Use state
You are using a class attribute witch will not trigger a re-render when changed. In react a component will re-render when it receives new props or when the state is changed (there's also a way to force it but best not to do that).
Example using the state to trigger a rerender
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Template from './src/components/Template';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state ={foo: "this is a test"};
}
changeMe = () => {
this.setState({foo:'changed'})
}
componentDidMount(){
this.changeMe();
}
render() {
return (
<Template foo={this.state.foo} />
);
}
}
When passing new props to a component it will also re-render (unless you implement componentShouldUpdate).
State explanation
So inside a react component you have a local state object in this.state it can be set in the constructor like this.state = {bar: 'foo'};. After the constructor has set the state it should only be changed with the this.setState() method.
Upon changing the state with setState the component will re-render with the updated values.
The state is not available outside of the component, at least not available as this.state because that would call the local state of the current component.
If you need to use a value from the state of a parent component you can pass it to a child. At that point it becomes a prop of the child accessible with
this.props
To change the value of state from a child component you should pass a function to the child that changes the state of the parent.
This process of passing state changing functions becomes increasingly complex as your app grows, I would suggest using a library like Redux to manage app state via actions and reducers. There is a steep learning curve but once you have a grasp of this modified flux methodology you will wonder how you ever lived without it.

How to set child component state using parent component props in React 16.3? [duplicate]

This question already has answers here:
Reactjs - Setting State from props using setState in child component
(2 answers)
Closed 4 years ago.
I have been working on building an app, where an uncommon scenario has raised for me to handle. I had to pass props to a child component from the parent, and the child component needs to set those props as its state.
<ChildComponent allProps={parentProps}>
<SomeChildren withProps />
</ChildComponent>
In the ChildComponent:
class ChildComponent extends Component {
state: {
someRequiredProps: null
}
setStateForThisComponent(propsToSet) {
this.setState({someRequiredProps: propsToSet});
}
componentDidMount() {
this.props.allProps()
}
render() {
//I know this is incorrect syntax
return (<div allProps={(propsArg) => this.setStateForThisComponent(propsArg)});
}
}
Usually, I can run this method from parent component using refs, but since this ChildComponent being a very heavily reused component, I do not want to go with the refs approach. Some other solutions suggested, that I create the state in parent component itself, and pass it as props to ChildComponent, but that is not what I am trying to achieve.
Please let me solve this issue of setting the child component's state using the parent's.
If you just need to set the initial props as the state, you can do it in constructor:
constructor(props) {
super(props);
this.state = {
someRequiredProps: props.someRequiredProps,
}
}
If the state must be updated whenever the prop updates, you need to use the new getDerivedStateFromProps method:
class ChildComponent extends Component {
state: {
someRequiredProps: null,
};
static getDerivedStateFromProps(nextProps, prevState) {
return {
// return the new state based on `nextProps`
someRequiredProps: nextProps.someRequiredProps,
};
}
Also note from the docs:
Note that if a parent component causes your component to re-render, this method will be called even if props have not changed. You may want to compare new and previous values if you only want to handle changes.

ReactJS this.state

I try to understand exmple from ReactJS:
class App extends Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
this.date = new Date(); // why not just this.date
}
render() {
return (
<div>
<h1>App Hello, {this.state.date.toLocaleTimeString()}.</h1>
<h2>{ this.date.toLocaleTimeString() }</h2> //same result
</div>
);
}
}
Why should I use this.state.date instead of just this.date?
For update this.date, I also may use setter, something like setDate().
If an property will not be changed during your component lifecycle generally you're right - result will be the same. But if you're going to change this property then your react component will not be aware of these changes if you decide to use this.someProperty because after the initial mounting React component is re-rendered only if setState (which updates this.state object) is called, parent component is re-rendered or forceUpdate is called. So if you want your component to be re-rendered when you change some property it should be stored in state object and updated using setState. Back to your example: if you use setDate to update this.date your component will not be aware of this action and will still show old value. If you use this:
this.setState({date: '2016'})
it will update this.state.date and React will be aware of this change and will re-render your component to show current date value.
this.state allows you to maintain state of the component during component lifecycle. It dosen't make any sense if you use this.state and never changed during component lifecycle.
to declare state you have to use:
this.state = {
'key': 'initial value'
}
to update state you have to use:
this.setState({
'key': 'your updated value'
});
It is bad practice to update state like this. this.state.key = somevalue.
Whenever your set any state of your component the render method will get called again to re render the component with updated value.
So for your question if your date is not going to change at any stage of component lifecycle then you can use this.date instead of this.state.date.
If it is changing and you want to use updated value then you can use this.state.
to initialize the state variable you just need to do
this.state = { `state variable name` : `initial value` }
And if you want to change/assign new value to the state variable, then
you have to do
this.setState( {`state variable name` : `new value`} )

Categories

Resources