ReactJS this.state - javascript

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`} )

Related

how to set an initial value on React.createRef() method?

I manage scrolling in my component with a ref that is the current scroll value. I cannot use state and setState, because when the user would be scrolling the setState method would make things really choppy and impossible. Hence, I decided to use refs that don't re render the component, but save the value when the component re renders.
My problem is that I cannot set an initial value for the ref. As I am using a class component I cannot use the simple useRef react hook that allows the initial value to be set easily... instead I use React.createRef() but when I put the value in the parenthesis () it doesn't seem to register it and is undefined until the user scrolls.
How can I fix that?
Here when the component updates (for example when a new message is sent), I want to make sure that it scrolls down to the newest message only if the user is not browsing older messages somewhere above.
To set the initial value of a ref created by React.createRef, do something like the following:
constructor(props) {
super(props):
this.scrollPosition = React.createRef();
this.scrollPosition.current = 201;
}
I do want to mention though, that since you're in a class component, you probably don't need a ref. In function components, refs are often used the way you're using them: to have a mutable object which persists from render to render.
But in class components, you already have a mutable object which persists from render to render: this. So you can probably ditch the ref and just do:
constructor(props) {
super(props);
this.scrollPosition = 201;
}
componentDidUpdate(prevProps, prevState) {
if (this.state.message === "" && this.scrollPosition > 200) {
// ...
}
}
I don't know why createRef doesn't take an initial value, but I wrote a replacement for it:
export function createRef<T>(initialValue: T): {current: T};
export function createRef<T>(): {current: T|null};
export function createRef<T>(initialValue?: T): {current: T|null} {
return {current: initialValue ?? null}
}

Why I am getting the old state values after the props change

I just want to understand why in the application I have following situation, below is my constructor of class component:
constructor(props) {
super(props);
this.state = {
tableAlerts: props.householdAlerts,
initialAlerts: props.householdAlerts
}
console.log('householdAlerts', props.householdAlerts)
}
in render function I have:
const { householdAlerts } = this.props;
My issue is that in constructor I got empty array, but in render funtion I have the data. Is it possible to get the data in constructor?
This is a very bad pattern when using the class component. You are ignoring any props updates when you copy the value into state. to manage it:
It requires you to manage two sources of data for the same variable: state and props. Thus, you need to add another render each time your prop change by setting it into state (don't forget to test on equality from prev and next values to avoid being in an infinite loop).
You can avoid setting the state each time your props change by using the getderivedstatefromprops lifecycle method.
So the recommendation is: just use the props; do not copy props into state.
To learn more why you shouldn't, I highly recommend this article.
It is not recommended to set your initial component state in the constructor like so because you gonna lose the ability to use { setState } method after to update this property/state.
The best practice is indeed to refer directly to the prop with { this.prop.householdAlerts }, and keep the state usage for local (or in child components} cases.
if anyhow you want to store props in component state for some reason, call it in lifeCycle -
componentDidMount() {
const { tableAlerts, initialAlerts } = this.props;
this.setState({ tableAlerts, initialAlerts });
}
Hagai Harari is right. Nevertheless, your actual problem seems to be that during your initial rendering the array is empty. Can you ensure that the array has some items, when your component is rendered for the first time?
First rendering -> calls constructor
<YourComponent householdAlerts={[]} />
Second rendering -> updates component
<YourComponent householdAlerts={[alert1, alert2, alert3]} />
If you want initial state to have the prop value.Try something like this with 'this' keyword
constructor(props) {
super(props);
this.state = {
tableAlerts: this.props.householdAlerts,
initialAlerts: this.props.householdAlerts
}
console.log('householdAlerts', props.householdAlerts)
}

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.

React.js: Changing the child state also changes its proptype passed by parent

The issue I'm having is that when I change the state of the child component, the state of the parent component is also changed. I'm passing the prop to the child as so:
{this.state.users.map((user, index) => (
<UserRow
key={user.id}
user={user}
removeUser={this.removeUser}
getUserInfo={this.getUserInfo}
finishFunc={this.finishChanges}
/>
))}
And then assigning that prop to the a state of the child like this:
state = {rowUser: this.props.user};
However, when I update this child state, it seems that the parent state updates without re-rendering.
I'm displaying the information through the prop, not the state
<div>{this.props.user.rules.length !== 0 ? this.props.user.rules.length : null}</div>
You can see the initial display here
When I click on the "Add" or "Remove" buttons, the state and display changes. You can see the change in the image here
I'm confused on how changing the child's state updates the parent's state without me specifically passing the data back to the parent. I also don't see how it is changing the display since I'm using the prop, not the state.
You can see all of my code in a sandbox here. You can see the permissions number change whenever you click the "add new permission" and "remove permission" buttons, which I don't want to happen.
How can I avoid having the parent state automatically change when I change the child state?
The Problem:
The issue lied in how I was changing the child's state state = {rowUser: this.props.user}. I was mutating the data and resetting the state like this:
addPermission = () => {
const tempUser = { ...this.state.rowUser };
tempUser.permissions.push({
property: "select permission",
info: "type info..."
});
this.setState({ rowUser: tempUser });
console.log("[UserEditingRow.js] permission added");
};
The Solution:
Changing the data directly (i.e. tempUser.permissions.push({...});) was causing a problem. I used React immutability helper to help update the child's state more specifically. So, the new function would be:
addPermission = () => {
this.setState(
update(this.state, {
rowUser: {
permissions: {
$push: [{property: 'select rule', info: ''}],
},
},
}),
);
console.log('[UserEditingRow.js] rule added');
};
Setting the state with immutability helper allowed the child component's state to change without updating the parent component's state and solved my problem.
After reviewing your Sandbox, the issue is you are assigning props.user to state.rowUser and is creating a pointer to the parent components value. This means whenever you change this.state.rowUser it will actually be updating the reference to props.user.
Solution
Use the constructor() to bind the props.user to state.rowUser, this is the recommend approach, additionally use the spread operator to create a new instance of the object passed into the component, this will ensure no pointer is created.
https://reactjs.org/docs/react-component.html#constructor
constructor(props){
this.state = { rowUser: ...props.user }
}
Additionally
Looking quickly at the sandbox I also found a few other anti-patterns that will be causing you some issues.
You are updating state directly Line 76 to 84 in UserRow then calling setState, what you should be doing is using the spread operator to clone the object const tempUserRow = { ...this.state.userRow } then manipulating the object and set it in state via setState. This will ensure your shallow compare in shouldComponentUpdate passes and re-renders your component.
You are using shouldComponentUpdate which is good, but your not doing anything that React.PureComponent does for you out of the box, so you should change extends React.Component to be extends React.PureComponent
https://reactjs.org/docs/react-api.html#reactpurecomponent

React child component state not updating when new props are passed

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.

Categories

Resources