Simple question: say that using React.Component I have such a code:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
}
What will be the equivalent of this using the createReactClass approach?
Edit:
I've read that I can use getInitialState for initial state, but is it a good place for creating refs?
What you are looking for is something like an initialize method, where you have access to component instance and is called before render.
The closest method I can think of is:
unsafe_componentwillmount
UNSAFE_componentWillMount() is invoked just before mounting occurs. It is called before render(), therefore calling setState() synchronously in this method will not trigger an extra rendering. Generally, we recommend using the constructor() instead for initializing state.
(Emphasis mine).
React docs specifically recommends using constructor instead of this life cycle hook, but if you don't have access to "constructor" itself then this is the closest thing imo.
Related
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
I am reading this and it says:
When a component is purely a result of props alone, no state, the
component can be written as a pure function avoiding the need to
create a React component instance.
What's the difference between a component and a component instance ?
Are they the same ?
EDIT:
What is the difference between Component and Component Instance ?
How do they relate to each-other ?
Conceptually ?
How are they represented in computer memory? How does the representation differ ?
What is a component and what is an instance of that component ? (In memory.) What kind of JS Object ?
Instance in what sense ? Object oriented sense ?
Is it true that every component can have (one or more) instance(s) ?
How many instances can a component have ?
Does it even make sense to say that an instance can be created for a/every react component ?
How are react component instances created and how are components created ?
Reason for asking:
I am trying to create a concept map of react to clarify the terminology and how they relate to each other.
Here is a draft:
The basic difference is, when it a Component, React will run/add all its Lifecycle methods. This will be useful when you have state in your component. When you use this component, React will create a React Component Instance which will have all the lifecycle methods and other hooks added to it.
class App extends React.Component{
...
}
In some cases, you won't use state. In those cases, adding all those lifecycle methods are unnecessary. So, React gives you an way to create an component which will have render alone. It is called PureComponent. When you use this, there is no need to create a new Component Instance because there is no lifecycle methods here. It'll just be a function which can take props and return React Elements.
class App extends React.PureComponent{
...
}
Hope this helps!
[Update]
What is a Component and a Component Instance?
Technically, a Component in React is a class or a function.
Example:
class App extends React.Component{
...
}
//stateless component
const App = (props) => {
...
}
When you use that component, it'll be instantiated, more like new App(). But, React does it by itself in a different way.
For Example:
render(){
return <App/> //Instance of the component App
}
Instances are required because, each instance can perform individually. Instances are a copy of original class.
Simple answer is, components will be a Class and component Instance will be the copy/instance of the class and will be used in render
Hope this explains!
A "React component instance" is just an instance that was created from a previously defined class component. See the example below (es6/JSX) which contains both props and state:
class MyComponentClass extends React.Component {
constructor(props) {
super(props);
// Set initial state
this.state = {
example: 'example'
};
}
render() {
return <div>
<div>{this.state.example}</div>
<div>{this.props.example}</div>
</div>;
}
}
If you have no need for state in your component you can use a pure, stateless, functional React component like so:
function MyStatelessFunctionalComponent(props) {
return <div>{this.props.example}</div>;
}
Here is some more information about stateless React components when they were introduced in React v0.14. Since then you have the ability to use hooks starting in React v16.8, which allow you to define a functional component that has state or makes use of the component lifecyle.
As mentioned in some other comments, there are many performance benefits when using stateless components. These types of components are perfect for when you want something purely presentational as an example.
Since there’s no state or lifecycle methods to worry about, the React team plans to avoid unnecessary checks and memory allocations in future releases.
I'm using ReactJS in Typescript. Do I need the "Constructor" code below? It works fine without it and I looked at the trans-piled JavaScript and it appears to add it in automatically anyway.
interface myProps {
children?: any;
}
class MyButton extends React.Component<myProps, {}> {
constructor(props: myProps) { //Needed ???
super(props);
}
render() {
return (<div>
<button>
{this.props.children}
</button>
</div>);
} //end render.
} //end class.
No, you don't need to.
In fact, you could write a simple component like this as a function.
const MyButton = (props) => {
return (
<div><button>{props.children}</button></div>
);
};
As mentioned in the accepted answer:
const MyButton = (props) => {
return (
<div><button>{props.children}</button></div>
);
};
This works but it's not the same - not at all. This is constructing a StatelessComponent (without state, without lifecycle-hooks, it's just a plain function, returning only the JSX or the part of the "render" function).
If you need state or lifecycle-hooks, you must extend from React.Component - like it is already done in the question.
To actually answer your question - yes constructor is needed. That's because you are extending of an existing class which is already asking for an initial property ("props") that must be given on the construct -> because it is a react class, react will call internally something like
new MyButton(props);
He will always give you the props object into the instantiated Component. Now, by extending this existing react component class, you must achieve the same - or you will end up missing your "props" object. To make it possible, that one can still pass "props" to your newly defined component, you have to define the props also on your constructor, so you have to write this:
constructor(props: myProps) { .. }
Otherwise, you can't pass anything when you call "new MyButton(props)" -> well, this will not except or bring an error, but "props" will just be "null" in your further code of "MyButton".
Last but not least - you have to call the "super" in an extended version.
super(props);
Otherwise, you won't pass the given "prop" object to the basic class which you're extending from. Here, it could work too without it, but - then "props" will be "null" in any code of "React.Component" itself.
So, yes it is needed!
Basically you can answer this question yourself by simply using a debugger - just open your web-dev tools, jump to your component code, make a breakpoint in your constructor and watch closely what is happending and what is passed ;) Then, delete the property in the constructor once.. and remove the super call once... how will it except/break apart? ;)
Update:
The other question you can always ask yourself when creating new react components: do i need state? do i need lifecycleHooks?
If one of those is yes, you'll have to extend from React.Component - because only the basic class is giving you this sugar. But as can say, no, i don't need any of those - always go with a StatelessComponent:
const MyComp = (props) => (
<div></div>
)
And try to avoid local component states too - you can do much better with one global state trough react-redux and selectors ;)
Constructor and Super are no longer necessary in react. State can be defined without a constructor and lifecycle hooks also work fine. This is a result of Babel transpiling the components that way: http://2ality.com/2017/07/class-fields.html
In traditional ES6 this is currently not the case.
Today, the constructor is needed only when you must initialize a component with a state from props.
import React from 'react'
class AChildComponent extends React.Component {
constructor(props){
super(props)
this.state = {
myState: props.myStateFromProps
}
}
render(){
return(
<div>
<p>{this.state.myState}</p>
</div>
)
}
}
export default AChildComponent
Out of curiosity, I just wanna know what will happen if I use setState() function in constructor of a Class in React Native or ReactJS?
Such as:
constructor(props) {
super(props);
this.setState({title: 'new title'});
}
what's the lifecycle of React gonna happen?
I haven't read the code inside React. I am afraid it will some any damage when I write it that way.
What setState essentially does is to run a bunch of logic you probably don't need in the constructor.
When you go state = {foo : "bar"} you simply assign something to the javascript object state, like you would any other object. (That's all state is by the way, just a regular object local to every component).
When you use setState(), then apart from assigning to the object state react also rerenders the component and all its children. Which you don't need in the constructor, since the component hasn't been rendered anyway.
Error Message would be
Uncaught TypeError: Cannot read property 'VARIABLE_NAME' of null
Please see the following two jsfiddle snippets.
Case 1) Working solution jsfiddle
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'world'
}
}
render() {
return <div>{this.state.name} </div>
}
}
ReactDOM.render(<Hello />, document.getElementById('container'));
Case 2) Not working solution
class Hello extends React.Component {
constructor(props) {
super(props);
this.setState({
name: 'hello'
});
}
render() {
return <div>{this.state.name} </div>
}
}
ReactDOM.render(<Hello />, document.getElementById('container'));
This happens:
-constructor (starts setState)
-render (fails - because state has no property 'name' yet)
-name is added to state (but too late - it has already rendered)
Conclusion:
My rule of thumb, inside constructor uses this.state = {} directly, other places use this.setState({ });
Constructor: Constructor is Used to initialize the state.
State : Components that contain local state have a property called "this.state".
SetState: React components have a method available to them called setState Calling "this.setState" causes React to re-render your application and update the DOM.you can also track of prevstate in setState
If you use setState in constructor you would get error like this:Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component.
React have not restricted the use of setState in any lifecycle event.
React official docs link for state update
It was told that you cannot access state directly outside constructor .
So you are free to call setState anywhere .
Technically setState is meant to update the existing state with some new value passed within ,which react react handles in the next update cycle through batch process,so using console.log of state right after setState will give the stale value.
Now lets focus on what if we call setState in the constructor .
React will prepare the batched piece of code with the new state passed into the setState and trigger a update .
While react have not stopped you from doing this ,however it knows that doing so might land you up in trouble so leaves a nice message for you
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the component.
React proceeds with further loading of component but the update never reflects for this batch.
So conclusion : If you try to do so you might not end up with error ,but you will have to bear the undesirable behavior as these updates will reflect no where even if triggered .
export default class Printer extends React.PureComponent<PrinterProps> {
static contextType = PrinterContext;
constructor(props: PrinterProps, context: PrinterInterface){
super(props, context);
this.PrinterInfo = getPrinterInfo(this.context);
}
I need to pass context to super to be able to access it within the constructor.
Passing of context to constructor is not there in their latest documentation -
https://reactjs.org/docs/context.html
but it is there in the legacy api documentation.
https://reactjs.org/docs/legacy-context.html#referencing-context-in-lifecycle-methods
Since passing context to super will be deprecated in versions 17 and above, what is a way to be able to accees context within passing it to super in constructor ?
Thanks!
Since passing react context to super will soon be deprecated, you won't be able to use the context in constructor. Another point would be, I've seen functional component is more preferred way of creating components (this is highly discussion topic)
I would recommend using functional component and then simply use useEffect or useContext hook if it makes sense in the overall situation.
You can follow the same approach in the class component as well for eg. using react lifecycle method componentDidMount() since context will be available once component is mounted, this lifecycle method makes sense to use context and call the method getPrinterInfo() in it.
If you are not planning to update the react to 17, you can use the code that you have written since it is working, but if you want to update the react in future and want to work with it follow the other approach.
export default class Printer extends React.PureComponent<PrinterProps, State> {
static contextType = PrinterContext;
context!: React.ContextType<typeof PrinterContext>;
constructor(props: PrinterProps){
super(props);
}
componentDidMount() {
this.getPrinterInfo();
}
getPrinterInfo = () => {
// you should have access to this.context
}
}