Today I did a review for my colleague and I found a function definition that interested me. He implemented a function inside a react component with default parameter values if the function call is not provided with an argument. He used a state value as a default parameter.
It looked like this example:
class CustomComponent extends React.Component {
constructor(props) {
this.state = {
loadedData = [], // array of objects
};
this.filterDates = (fromUtc, toUtc, loadedData = this.state.loadedData) {
// do something with 'loadedData' based on time range 'fromUtc' and 'toUtc'
}
}
}
He could not provide me a good explanation. Only that it works in his implementation.
I have always used 'static' default parameter values (like [], number, etc.)
I am curious if it is ok to use some kind of 'dynamic' default parameter, which changes when state changes.
Is it ok to write it like this? Could be there a problematic case?
According to Airbnb Javascript Style Guide -es6 default parameters this approach is good. However I question assigning default value to state which is mutable by definition unless it's desired effect. Default parameter shouldn't be mutable. Personally I see such approach for the first time and I think it's not intuitive but maybe it's just my experience.
In my opinion code below is cleaner, easier to understand and less buggy:
class CustomComponent extends React.Component {
constructor(props) {
this.state = {
loadedData = [], // array of objects
};
this.filterDates = (fromUtc, toUtc, loadedData = []) => {
// do something with 'loadedData' based on time range 'fromUtc' and 'toUtc'
}
}
}
In the constructor, this.state is a simple object without any magical feature of states. So, loadedData = this.state.loadedData is the same as loadedData = [] but the second one is more readable.
Related
Please advise how to solve the following problem:
There is a component that outputs some counter:
#observer class MyComponent extends React.Component {
render() {
const data: MyData = new MyData();
return <div>{data['counter']}</div>;
}
}
The counter is stored in a trackable (via mobx #observable) singleton:
export class MyData extends ISingleton
{
#observable data: any = {}
}
At some point the counter (when MyComponent is already created) the counter is set
let data: MyData = new MyData();
data['counter'] = 123;
But MyComponent is not redrawn, although the component seems to track variable change (via mobx #observer and #observable)
Please advise where is the error and how can it be fixed
In mobx you need manipulate the state of observers within actions.
You should extend your singleton to look something like this:
export class MyData extends ISingleton
{
#observable data: any = {}
#action
setCounter(val: number) {
this.data = val
}
}
Then call the setCounter-Method instead of manipulating the state directly.
Please take note that as of version 6 of MobX it is discouraged to use the decorator API (see this for more information).
Every render creates a completely fresh new data object, effectively resetting it. So you want to store data at the class instance, not inside the render. Also the data you are modifying is completely different data than the data you are rendering (that is what the new keyword does). Probably you want to read up a little on how objects, classes & instances work in JavaScript in general
I want to know the conditions where by a constructor is not needed in a component's class declaration. I figure it is for stateless components, but are there any other reasons? Would not having any functions inside the component (besides life cycle functions) be one, for example?
I think it would be appropriate to just leave here an excerpt from react docs (emphasis mine):
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.
I usually only add a constructor if the component has a internal state I need to set up before it is used, otherwise I leave out the constructor. Having functions in the component doesn't affect my decision in this regard
You don't actually need a constructor at all in any case if you use the babel stage-2 preset, because it provides the class properties that effectively replace its usage:
class Component extends React.Component {
constructor() {
this.state = {};
this.handleClick = this.handleClick.bind(this);
}
handleClick() { console.log('click'); }
}
becomes
class Component extends React.Component {
state = {};
handleClick = () => console.log('click');
}
Ignoring this, a constructor is needed only if you need to bind component methods to its context, or if you need to initialize the state property.
Another case is if you need to do something with the this.props class property, but this is considered an anti-pattern by React.
class Component extends React.Component {
constructor() {
this.state = {
// don't do this! anti-pattern! duplication of source of truth!
duplicatedState: this.props.myName,
};
}
}
I'm trying to adapt code from a SO answer, with functions and variables written as below:
const getIntervals = n=> availability=> {
}
let availability = [
]
Are those normally fine to use in a react class (see below) or do they need to be rewritten?
class Calendar extends React.Component {}
The reason for asking is that I use a React implementation for Rails and do get an error including that function and variable naming pattern.
Pure Functions, which dont modify the passed value, are always fine to use anywhere.
Its also fine to use them in a React Class directly, but common functions like string modifications, array sorting algorithms, which you are using a lot across your app and classes should go in a separate module like
// my-helpers.js
export const countKeysInObject = (data) => {
if (typeof data !== "object" || Array.isArray(data)) return 0;
Object.keys(data).length;
}
some other file..
import { countKeysInObject } form 'my-helpers'
// And you can use it everywhere..
If you are using class and extending Component, you can use simple methods for most things:
class Calendar extends React.Component {
constructor(props) {
this.date = props.date;
}
render() {
return <span>{this.date.toString()}</span>;
}
}
Calendar.propTypes = {
date: React.PropTypes.date.isRequired
};
You cannot use methods for propTypes or anything that would be an initial field if you were using an object literal. Those need to be attached after the class has been declared (propTypes) or in the constructor (initial state).
I see a number of questions on here relating to this same issue, but it seems none match the issue I'm having, and are a bit more complex.
I am in the process of learning ReactJS and React Native. I'm in the midst of reading and following the code examples from "Learning React Native" book here: https://github.com/bonniee/learning-react-native
For some reason, calling this.setState in the code below when the handleTextChange function is called, causes the "this.SetState is not a function." error. My question is why? Unlike other questions about this same issue, I don't believe my call to this.stateState is buried in a callback function or if statement. Why is it undefined?
Here is my code:
class WeatherProject extends Component {
constructor(props) {
super(props);
this.state = {
zip: "",
forecast: null
};
}
_handleTextChange(event) {
this.setState({zip: event.nativeEvent.text});
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
You input {this.state.zip}.
</Text>
<TextInput
style={styles.input}
onSubmitEditing={this._handleTextChange}/>
</View>
);
}
}
Do not use bind inside a render. bind is a rather expensive operation and should only happen once. you have two options:
either bind the function in the constructor:
this._handleTextChange = this._handleTextChange.bind(this);
or use arrow function:
onSubmitEditing={(e) => this._handleTextChange(e)} />
Edit
Apparently arrow functions inside render is also a bad practice (Thx to Adam Terlson in the comments and answer below). You can read eslint docs which states:
A bind call or arrow function in a JSX prop will create a brand new function on every single render. This is bad for performance, as it will result in the garbage collector being invoked way more than is necessary.
Using arrow functions is obviously not as bad as using bind, but nevertheless should be avoided.
Regarding arrow function you also need to change _handleTextChange(event) function. Other answers didn't talk about how to change normal function to arrow function.
You need to change handler function, from:
_handleTextChange(event) {
this.setState({zip: event.nativeEvent.text});
}
To:
_handleTextChange = event => {
this.setState({zip: event.nativeEvent.text});
}
The issue is context binding, as identified in the other comments and answers here.
However, the performance of bind itself is a non-issue. The way-more-relevant issue is that using bind or arrows in your render methods creates a new function on each render, resulting in a change of props for the child that receives them, forcing a re-render.
You have two viable options:
class WeatherProject extends Component {
constructor(props) {
super(props);
this._handleTextChange = this._handleTextChange.bind(this);
}
// ...
}
Or you can use the class property notation and assign arrow functions if you're using the babel plugin for it.
class WeatherProject extends Component {
constructor(props) {
super(props);
// ...
}
handleTextChange = (event) => {
this.setState({zip: event.nativeEvent.text});
}
// ...
}
I strongly recommend you use the eslint package with the react recommended rules enabled. It will catch errors like using bind/arrows in your render, as well as tell you that underscore prefixed functions are ugly and totally not necessary in React. :)
this.setState is not a function--- Solved using this
let that = this;
that.setState({membersArray:response.data})
I had the same problem when trying to set the state. when I bind the function inside the constructor, the issue gets solved. check with below binding
constructor(props) {
super(props);
this.state = {
zip: "",
forecast: null
};
this._handleTextChange = this._handleTextChange.bind(this);
}
I am unclear about the use of this.state in React components. While I can create this.state.myvar, why should not I just create this.myvar?
class MyComponent extends Component {
state = {myvar: 123};
render() {
return <span>{this.state.myvar}</span>;
}
}
or
class MyComponent extends Component {
myvar = 123;
render() {
return <span>{this.myvar}</span>;
}
}
I realize that there are helpers like this.setState, but at the end this.state is just a convenience, right? Or does it play a bigger role in React? Should I avoid setting properties directly on this to store my state? If so, why?
No, in fact; rather wrong.
this.state in a react component is a special React-backed container that is only acknowledged as having been updated when you use setState, which triggers a re-render, which might cause DOM updates (or not, depending on what React's diff algorithm sees happening in the JS virtual dom).
You can, of course, also use object properties bound to this, but changing them does absolutely nothing for the component itself. React doesn't look at your full component instance for changes, it only looks (internally) at the state, and (when changed by parents) at the props.
As such, you can't "create" things like this.state.myvar and then expect them to actually exist from lifecycle function to lifecycle function: as a special management construct, any values you tack onto state outside of proper this.setState(...) calls have undefined behaviour. They might exist, or they might not. If you really are working with the internal state, then you need to signal changes via this.setState({ myvar: value }).
Of course, that doesn't mean you can't use this.myvar, that'll work fine, but changing it will not "do" anything other than literally just that.
When would you use this.val instead of state? When your component has to perform operations that lead to an "intermediate" state, neither being one renderable state, nor the next. For instance, when code can update a state value multiple times between renders, during those changes your component is in an intermediate state, and so you don't want it to re-render. In fact, expecting it to can lead to huge bugs:
...
doTest() {
this.setState({ val: this.state.val+1 });
this.setState({ val: this.state.val+1 });
this.setState({ val: this.state.val+1 });
},
...
This code will not yield a this.state.val that's 3 higher than before, because state updates are queued, and overwrite each other. Only the last instruction before a re-render "wins". So in that case you'd need something like:
...
doTest() {
var localval = this.state.val;
localval++;
localval++;
localval++;
this.setState({ val: localval });
},
...
And then if we also need that value accessible outside of this function, then we finally have a legitimate use for a this property:
...
doTest() {
this.accessibleval = this.state.val;
this.updateValueAFewTimes();
this.setState({ val: this.accessibleval });
},
...
this.state plays a larger role than you realize.
Setting state via this.setState triggers the component to re-render (among other things). Otherwise, React would have no way of knowing when something it depends on changed.