React Native state variables not getting updated from props - reactjs

I'm passing 3 props - numLikes, id and userLiked to my class and I want to set my state variables initially before any render occurs.
Ideally, the values of state variables should be equal to their props counterparts but this isn't the case.
This is my code:
export default class LikeButton2 extends Component {
constructor(props) {
super(props);
this.state = {
numLikes: props.numLikes,
id: props.id,
userLiked: props.userLiked,
isloading: true,
};
}
//....
}
I used React Native Debugger to check for the variable values and the variables "numLikes" and "userLiked" were not getting updated. Attached is the proof for the same:
I also tried using spread syntax.
This is my code:
export default class LikeButton2 extends Component {
constructor(props) {
super(props);
this.state = {
...props,
isLoading: true,
};
}
//....
}
Although this also resulted in undesired values for the state variables. Proof for this in RN Debugger:
How can I correctly update the values?

In this exemple, when the component is mounted to be displayed, the state is set to be equal to the props. The constructor is only called at that time.
After that, if the props change, the state won't be updated to match them. If you want your state to be updated when the props change, you need to use the method ComponentDidUpdate:ComponentDidUpdate(prevProps,prevState).
It will be called every time the props or the state change. You can then compare the current props (this.props) and the previous props (prevProps) to set your state accordingly.
If you just want to use your props as they are you can just use this.props, it will always reflect the value you give to the component.
If I have <Component number={myNumber}/> somewhere in the code, Component will rerender every time myNumber changes, and the value this.props.myNumber in Component will be accurate.
export default class ComponentTest extends Component {
constructor(props) {
super(props);
}
render() {
return <div>{this.props.number}</div>
}
}

Related

Why I cannot reset state of react component?

I try to reset react component state by using preserved initial variable but this is not worked. What is the problem here?
First have I created a base state variable "baseState" then I will used to reset react state. but it not worked,
Here I create a sample program below,
class Product extends Component {
constructor(props){
super(props);
this.baseState = {isLoggedIn: true, loader:false, inputs : { name :null, category_id:null }};
this.state=this.baseState;
}
render {
(<Form>{/* form inputs */}</Form>)
}
// this is a onChange select event for update input values.
selectInputChange(e){
let stateInputs=this.state.inputs;
stateInputs[e.target.name]=e.target.value;
let inputErrors=this.state.errors;
let error=this.validateFormInput(e.target.name,e.target.value);
inputErrors[e.target.name]=error?error:null;
this.setState( prev =>{
return{
...prev, inputs: stateInputs, errors:inputErrors
}
});
console.log(this.baseState); // here value is updated.
}
handleFormSubmit(){
// after insert record through axios. then i try to reset like
this.setState(this.baseState);
// but state is not updated here.
}
}
export default withRouter(Product);
I am beginner to reactjs so help me to solve the problem, thank you!
You're making this.state point to the same object as this.baseState, so a change to one is a change the other because there is no difference: they're literally the same object. As you're discovering, this.setState(this.baseState) will do nothing, because you're updating the current state with itself, and is the same as saying this.setState(this.state).
If you want a state that you can truly reset to, use a function that generates a new object each time you call it:
class Product extends Component {
constructor(props){
super(props);
this.state = this.getInitialState();
}
getInitialState() {
return {
isLoggedIn: true,
loader: false,
inputs: {
name: null,
category_id: null
}
};
}
...
And then whenever you want to reset your component, you can call this.setState(this.getInitialState()), which will reset all the properties that are relevant to your initial state (while leaving anything else entirely untouched - you won't be removing any state values not covered by the initial state object).

Props passed to child component are empty in constructor and componentDidMount()

I am just testing out various ideas and I do have experience using React, however, I seem to be totally missing something here.
In the ParentComponent I set initial state of an array within the constructor.
In componentDidMount() I then call a function to get some mock data which is just an array of objects. The function can be seen within ParentComponent. I then set the state using the mock data.
In the render() function of the ParentComponent I pass the array of objects as a prop to the ChildComponent.
In the ChildComponent when I try accessing the props within the constructor or within componentDidMount() the array is empty.
If I access the props in the render() function of the ChildComponent they are visible.
In React Dev Tools the props are clearly visible within the ChildComponent.
Does anyone have any idea why this behaviour is happening?
I need to be able to do some one time calculations on the data when the component is first mounted. Does anyone have any suggestions on how I can do this?
In all other languages I am aware of, if you pass arguments to a constructor they are immediately available.
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentDidMount() {
const mockData = this.getMockData();
this.setState({
data: mockData
});
}
render() {
return (<ChildComponent data={ this.state.data }/>);
}
getMockData = () => {
return [
{
key: 1,
name: "Test1",
price: "£0.00"
},
{
key: 2,
name: "Test2",
price: "£0.00"
}
]
}
}
export default ParentComponent;
class ChildComponent extends Component {
constructor(props) {
super(props);
console.log(props.data);
}
render() {
return (return some stuff here);
}
}
export default ChildComponent;
As you are populating the data array in componentDidMount lifecycle method, data array is initially empty when it is passed as a prop to the ChildComponent. This is because componentDidMount is called after the initial render.
So, when ChildComponent is mounted as a result of initial render of ParentComponent, data prop passed to ChildComponent is an empty array. Once the ChildComponent has mounted, then ParentComponent is mounted. At this point, componentDidMount method in ParentComponent is called.
You see the empty array in the constructor because it is only called once during the initial render of the ChildComponent. render() method in ChildComponent sees the latest data array because when componentDidMount is called in the ParentComponent, it updates the state, ParentComponent re-renders, leading to the re-render of ChildComponent with updated data array as a prop BUT the constructor in the ChildComponent is not called again, only render() method executes when ChildComponent re-renders.
To see the updated data in the ChildComponent, you can use one of the following options:
Use the data prop in the render() method
Use componentDidUpdate() lifecycle method
componentDidUpdate(prevProps, prevState) {
console.log(this.props.data);
}
Logging data prop in the render() method will first log the empty data array because of the initial render and then it will log the updated data array due to re-render of the ChildComponent.
Logging data prop in the componentDidUpdate() method will only log the updated data array because it is not called during the initial render of the component.
You assign a data to this.state.data in componentDidMount which is called as React completed to updated the DOM , so the props actually passed to the child component but their values in the moment of childComponenet construction still null because the componentDidMount still not called, and therefore the "this.getMockData()" not called yet

How to update the value of a prop upon a state change in React

I have a parent React component (MainComponent) that renders a child component (theKeyComponent) and passes a constant as a prop (myID). The parent component also tracks the state 'activeLink'.
import theKeyComponent from "../components/theKeyComponent.jsx";
export default class MainComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
activeLink: '#section1'
}
}
render(){
const myId = this.state.activeLink === '#section1' ? '0001' : '0002';
return(){
<theKeyComponent myID={myID} />
<otherComponent otherPropsHere={otherProps} />
}
}
}
Here's a bit from inside the KeyComponent
export default class HelpScoutBeacon extends React.Component {
static propTypes = {
myID: PropTypes.string.isRequired
}
componentDidMount() {
myAPImethod('init', this.props.myID);
}
render(){return(){}}
}
I want to change the value of the constant myID depending on the value of 'activeLink'. This is not a problem when both components are mounted for the first time. However, when the value of 'activeLink' changes 'myID' doesn't change since the child component is already mounted.
I'm struggling to see what would be the 'React way' of doing this. Should 'myID' be set as another state and the function that sets the state for activeLink should include another one to set the state of myID? Or is this overcomplicating things and there's an easier way to re-render only that particular child component so that it considers the new value of myID.
I'm new to React so I was hoping I could get some clarification form SO community.
This is not a problem when both components are mounted for the first
time. However, when the value of 'activeLink' changes 'myID' doesn't
change since the child component is already mounted.
The issue is with regards to how you handle the trigger of the API call.
componentDidMount will only trigger when the component was initially mounted. This will not be triggered if a state is updated. You are going to want to use componentDidUpdate React Lifecycle as well in augmentation to componentDidMount. componentDidUpdate will trigger when your activeLink state changes because you pass it as props to theKeyComponent
componentDidUpdate() {
myAPImethod('init', this.props.myID);
}
Reference: https://reactjs.org/docs/react-component.html#componentdidupdate
import theKeyComponent from "../components/theKeyComponent.jsx";
export default class MainComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
activeLink: '#section1'
}
}
static getDerivedStateFromProps(props, state) {
if (props.activeLink !== state.activeLink ) {
// updating the state here if there are any changes in props.
return {
activeLink: props.activeLink,
};
}
// Return null if the props hasn't changed
return null;
}
use getDerivedStateFromProps hook to find out changes of props. you need to update the changes of props in state.
render(){
// now, here your state is updated.
const myId = this.state.activeLink === '#section1' ? '0001' : '0002';
return (
<theKeyComponent myID={myID} />
<otherComponent otherPropsHere={otherProps} />
)
}
Any suggestions are welcome.

How can I force a component update with non-component class objects?

I have a React component with a non-component class object in its state. Right now, I am using the React component to display some of the other object's fields. I also want to make it so that every time the displayed field in the non-component class object changes, the react component is re-rendered to show that new updated field. How can I do this?
class ReactComponentClass extends React.Component {
constructor(props) {
super(props);
this.state = {
myObj: new SimpleJSClass()
}
}
render() {
return (
<div>
<p>Value: {this.state.myObj.value}</p>
</div>
);
}
}
class SimpleJSClass {
constructor() {
this.value = 1
}
}
I have heard of this.forceUpdate() but is that the only option? If it is possible, I would like to only update value instead of everything.
In React a component rerenders when its state changes. React will notice the change if the this.setState method is called.
You can create a class variable with the new instance
constructor(props) {
super(props);
this.simpleClassInstance = new SimpleJSClass();
this.state = {
myObj: this.simpleClassInstance
}
}
So when the class content upgraded call a setState with it.
this.setState({myObj: this.simpleClassInstance})
On the other hand I rarely use classes for data. I always use objects. Mutating the state is considered harmful. So calling setState with a new object when a data is changed is the common solution.

react-constructor or componentWillRecevieProps

i use the react for two month, something the react will do constructor, sometime the react will do componentWillRecevieProps.
eg:
renderGroups(){
return this.props.groups.map( (group, i) => {
return <PickerGroup key={i} {...group} onChange={this.handleChange} groupIndex={i} defaultIndex={this.state.selected[i]} />;
});
}
this write will do PickGroup constructor every time
Its confusing what you are asking here, but you mentioned React will receive a constructor() function and sometimes it receives a componentWillReceiveProps.
The purpose of constructors() in React is to initialize state. You initialize state when the component is first created. After that, you use state inside the render() method and then at some point in the future, you update that state with setState().
Unlike the constructor() function, the render() method is required for every single React component you create, otherwise, React will throw an error. The constructor() function is not required by React, but it is implemented in a class-based component like so:
class App extends React.Component {
constructor() {
}
// React says we have to define render()
render() {
window.navigator.geolocation.getCurrentPosition(
(position) => console.log(position),
(err) => console.log(err)
);
return <div>Latitude: </div>;
}
};
The constructor() function is particular to the JavaScript language, not to React.
In a JavaScript class, the constructor() function is the first function that is going to be called anytime an instance of this class is created.
Anytime we create a new instance of the App component and display it on the screen, this constructor() function is going to be automatically and instantly called before anything else.
Now, to get to what I believe is your question, this is not the only way to initialize state in React.
Before I get into the other way to initialize state, you ought to know that when we define the constructor method, it will automatically be called with the props object and yes this is the same props object you may have seen with functional components and with a class-based component it looks like this:
class App extends React.Component {
constructor(props) {
}
// React says we have to define render()
render() {
window.navigator.geolocation.getCurrentPosition(
(position) => console.log(position),
(err) => console.log(err)
);
return <div>Latitude: </div>;
}
};
When you use the constructor(props) function, there is one required step which is adding super(props);. Why do we have to add this?
Well, remember, I am talking about class-based components here and keep in mind that as a class-based component our App component above is extending or borrowing functionality from the React.Component base class here.
This base class has a constructor() function of its own that has some code inside of it, to setup our react component for us.
When we define a constructor() function inside our App class, we are essentially, overriding or replacing the constructor() function that is inside the React.Component class, but we still need to ensure that all the setup code inside of the React.Component constructor() function still gets called.
So to ensure that the React.Component’s constructor() function gets called, we call super(props);. super(props) is a reference to the parents constructor() function, that’s all it is.
We have to add super(props) every single time we define a constructor() function inside a class-based component.
If we don’t we will see an error saying that we have to call super().
The entire reason for defining this constructor() function is to initialize our state object.
So in order to initialize my state object, underneath the super(props); call I am going to write:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
// React says we have to define render()
render() {
window.navigator.geolocation.getCurrentPosition(
(position) => console.log(position),
(err) => console.log(err)
);
return <div>Latitude: </div>;
}
};
This is our state object that will eventually contain some different pieces of data, some different properties that are very important and relevant to the component being put together.
So lets say I initialized my state with a property called lat for latitude because this is a geolocation application I am working on, so now my initialized state looks like this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {lat: null};
}
I set the value of the lat property to null because I don't have a latitude yet and the latitude value will eventually be a number and in such a case when you know the value will be a number that you don't have yet, you can default it to be null.
Now, let's say we get an error on this app and we cannot get any data or state at all. We can add another property to state called errorMessage and we can default it to be an empty string like so:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {lat: null, errorMessage: '' };
}
Alright, now lets talk about the alternate to the constructor(), by changing this.state and refactoring to state = {lat: null, errorMessage: '' }; and then deleting the constructor() entirely.
class App extends React.Component {
state = {lat: null, errorMessage: '' };
}
This is equivalent to using the constructor() function and I can prove this by going to https://babeljs.io/ and adding the above code to the left panel and seeing how Babel translates it into a constructor() anyway and it translates it back to this.state = like so:
If you want to reproduce this yourself, please pay attention to what presets have been ticked on the left hand side of Babel tool.
Lastly, you mentioned the alternative to the constructor() would be componentWillReceiveProps, but the alternative is actually what I just implemented above.
Please keep in mind the difference between initializing state and utilizing props. Everything described above is initializing state which is what the constructor() is for.
In React, you will see different lifecycle methods called, but typically speaking what you will see most of the time is componentDidMount() which automatically gets called one time when the component first gets rendered on the screen and you can place logic in there to do initial data loading and other operations you might want to do one time when the component first boots up.
The other lifecycle methods you will typically see most often in addition to componentDidMount() is componentDidUpdate() and componentDidUnmount(), but these are not alternatives to initializing state.
The constructor() function is the only lifecycle method used for state initialization, yes, it is a lifecycle method and so is the render() method.
First of all Constructor and ComponentWillReceiveProps are used for two different purposes. As per the React DOCS
Constructor:
The constructor for a React component is called before it is mounted.
When implementing the constructor for a React.Component subclass, you
should call super(props) before any other statement. Otherwise,
this.props will be undefined in the constructor, which can lead to
bugs.
The constructor is the right place to initialize state. If you don't
initialize state and you don't bind methods, you don't need to
implement a constructor for your React component.
componentWillReceiveProps:
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.
React doesn't call componentWillReceiveProps with initial props during mounting. It only calls this method if some of component's
props may update.
So in general you may want to call take actions in your component when the parent component has sent different props, in this case you will use componentWillReceiveProps as well as the constructor to initialse the state with props.
In your case since your are using a map function you are again and again mounting the same component whenever the renderGroups function is being called and hence the constructor is called everytime instead of componentWillReceiveProps
I hope you, I was able to explain

Resources