Don't understand how JavaScript assign operator interacts with a function - javascript

My problem is that I don't understand how assign operator is interpreted and interacts with a function. Let me explain
I was changing the state of my component, and I was doing something wrong that I couldn't see until I realize it, of course.
My component:
class UpdateLifeCycle extends Component {
state = { src: urls[this.props.election] };
componentWillReceiveProps(nextProps) {
this.setState = { src: urls[nextProps.election] };
}
render() {
return (
<div>
<p>Selected {this.props.election}</p>
<img //
alt={this.props.election}
src={this.state.src}
witdh="250"
/>
<p>{urls[this.props.election]}</p>
<p>{this.state.src}</p>
</div>
);
}
}
What I was doing, and is wrong is this piece
this.setState = { src: urls[nextProps.election] };
and what I should be doing is passing it as a parameter instead of assigning it.
this.setState({ src: urls[nextProps.election] });
Why does the JS interpreter allow you to assign and object to a function, and where is that object being assigned?

TL;DR
setState is a variable which currently holds a function. There is nothing stopping you from changing its value and add an object there, because it is a variable. JavaScript was influenced by other functional programming languages and it is okay to assign a function to a variable.
Explanation
state is a special object in a React component, which you would access as this.state. setState is a special function in a React component, which allows you to modify the state. Actions are dispatched when the state is updated, however you should not directly modify the state of a React component, and should always use setState.
setState is another variable in a JavaScript class (Your React Component is a class-based component), which by default holds the function to modify the state variable.
You can override a variable in JavaScript and when you assign an object (the new state you wanted) to setState, you are changing the value of setState which can no longer now update the state variable.
You would find React's docs helpful.

Related

ReactJS How to pass a prop to a component in state

function App() {
const [currentPanel, setCurrentPanel] = useState(ProfilePanel); // ProfilePanel is a component
return (
<div className={styles.App}>
{currentPanel}
</div>
);
}
In code i set the component "ProfilePanel" to a "curentPanlel" state, then in App i change the component in state, and this render an another panel. the problem is that i dont know how to pass props when i render it like this.
i tried the {currentPanel()} but is return an error.
please help to find a method to solve this, or if this method to render a component in state are absolutly wrong tell how to do this another way.
the problem is that i dont know how to pass props when i render it like this
You'd do it by using an initial capital letter for the state member (CurrentPanel instead of currentPanel), and then using it as normal (<CurrentPanel someProp="some value" />). (It has to be initially-capped because that's how JSX knows it's supposed to be a component, not a tag name.) But, you'll struggle to set a different component function in state, because component functions are, well, functions, and when you pass a function to a state setter, it thinks you're using the callback version of the state setter and calls your function, rather than setting it in state.
If you absolutely have to hold a component function in state, wrap it in an object, but it's much more likely that there's a better solution to the overall problem you're trying to solve.

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

Is there a problem with setting React.Component state equal to a this.props value in typescript?

I am attempting to assign an object that is passed into a React.Component as a property to a state value like so,
state = {
rota: this.props.rota
}
render() {
// const { cleaning, chairs, creche, flowers } = this.state.rota;
const { cleaning, chairs, creche, flowers } = this.props.rota;
console.log(creche);
}
The commented out section where it get's the value of the state prints out an empty string, however the property values are correct. Am I doing something wrong when assigning the props.rota to the state.rota?
I am using typescript and have done exactly this in another place in my program - however the property being passed in was a value type (string) rather than an object type.
Thanks!
That's usually a bad practice.
Take your case.
You get a value as prop from a parent component.
The inner component will already be re rendered every time that this value changes in the parent component.
If you go with an approach like yours, what most probably you will do, is to change that value inside the inner component too (through state), whose changes will not be reflected on the parent component.
You are actually breaking the design pattern of uni directional data flow, on which react relies a lot on.
So my personal opinion is to lift up the state in this case and avoid such kind of situations. Use callbacks instead if you want to communicate changes to the parent, or use some state management (context, redux, etc..).
Or design a better solution using HOC or render props Components.
Even though #quirimmo has pretty much answered your question, if you want to do this sometime in the future, the easiest way would be to use a constructor function and pass the props in as a param, and then just set that as the default value of the state
class SomeComponent extends Component {
constructor(props){
super(props);
this.state = {
rota: props.rota,
}
}
}
This makes sure that the prop is actually available in the moment you want to set the initial state, since the constructor is the first function that is called in the component lifecycle.

What is the difference between React component instance property and state property?

Consider the below example
class MyApp extends Component {
counter = 0;
state = {
counter: 0
};
incrementCounter() {
this.counter = this.counter + 1;
this.setState({
counter: this.state.counter + 1
});
}
render() {
return <div>
<p>{this.counter} and {this.state.counter}</p>
<button onClick={this.incrementCounter}>Increment</button>
</div>
}
}
When I click the button I see both this.counter and this.state.counter are showing the incremented value
My question is why I have to use state? though react is capable of re-rendering all the instance properties
counter = 0;
incrementCounter() {
this.counter = this.counter + 1;
this.setState({});
}
In above snippet, just calling this.setState({}) is doing the trick, then why should I use this.state property for storing my component state?
state and instance properties serve different purposes. While calling setState with empty arguments will cause a render and will reflect the updated instance properties, state can be used for many more features like
comparing prevState and currentState in shouldComponentUpdate to decide whether you want to render or not, or in lifecycle method like componentDidUpdate where you can take an action based on state change.
state is a special instance property used by react to serve special purposes. Also in setState, state updates are batched for performance reasons and state updates happen asynchronously unlike class variable updates which happen synchronously. A class variable won't have these features.
Also when you supply a class variable as prop to the component, a change in this class variable can't be differentiated in the lifecycle methods of the child component unless you are creating a new instance of the variable yourself. React does it with state property for you already.
Both have different purpose. Rule of thumb is:
Use state to store data if it is involved in rendering or data flow (i.e. if its used directly or indirectly in render method)
Use other instance fields to store data if value is NOT involved in rendering or data flow (to prevent rendering on change of data) e.g. to store a timer ID that is not used in render method. See TimerID example in official docs to understand this valid case.
If some value isn’t used for rendering or data flow (for example, a timer ID), you don’t have
to put it in the state. Such values can be defined as fields on the
component instance.
Reference: https://reactjs.org/docs/react-component.html#state

ReactJS - setState fails to update property value

I have a property name in a React component. Using setState I am trying to modify this property. But assigning a new value to this property inside setState has no effect. Please find below my sample code.
export const Test = ({
name,
changeState = function (newName) {
this.setState({name: newName}, () => console.log('Name after setState # ' + name)); //prints old value
console.log(name); // Doesn't reflect changes. Prints old name
}
}) =>
(
<div>Some data</div>
)
User action will trigger a call to changeState(newName). I am calling setState inside changeState function.
After calling setState if I print the name variable to console it still holds old value.
How can I make setState assign a new value to name?
I am aware that setState is asynchronous and update to property may reflect after a delay. But in case of example code above name variable is never updated even after a delay.
I have implemented componentWillReceiveProps(nextProps) method and it gets called but it always receives old name. So I don't think the issue is related to setState being asynchronous.
I have created an example app to demonstrate the issue I am facing. The example app runs fine locally and I can reproduce the issue. But I am not being able to get the same example app code working in codepen. it's giving me some 'unexpected token' errors. But I hope looking at the code will help understand my issue better. The code is based on existing application structure.
Codepen example here.
In the code I have a Parent and two children Child1 and Child2. In Parent I have a function changeState defined which I am passing to Child1 as property. And to Child2 I am passing a 'name' property from Parent.
Clicking on 'Change Name' button in Child1 triggers a call to changeState function of Parent. Initial value of name in Parent is 'Robert'. changeState is invoked from Child1 with new name value of 'Tom'. But in changeState function assigning new value to 'name' using setState has no effect. I am calling a function to print the 'name' after setState has completed but it prints old name value and NOT the new one assigned in setState.
You are using stateless component, there's for, there is no state so the setState function doesn't affect anything.
there are 2 options to deal with it:
the easiest one, but most likely not the best option, just change your component to regular component:
export class Test1 extends React.componnent {
...
(the rest of your component)
}
the second option (usually a better one in my option) is instead of changing the state for the component, get an event from the parent component, and call the event where you wanted to call the setState, the event would contain an updating the value as requested, and the child component would receive it as prop, and your component wouldn't have to change to Container (a component with state)
Good luck!

Categories

Resources