Is there any side effect I do not see by doing this ?
class App extends React.Component {
hello() {
console.log("hello")
}
render() {
return <Layout app={this}>
}
}
So later on I can refer to this.props.app.hello (and others) from Layout ?
This is not safe.
React will not know how to watch for changes, so you may miss re-renders. React uses === to check for state changes, and App will always be === to App, even when state or properties change.
Take this example:
class App extends React.Component {
constructor(props) {
super(props);
this.setState({text: 'default value'});
}
hello() {
this.setState({...this.state, text: 'new value'});
}
render() {
return (
<div onClick={this.hello}>
<Layout app={this}>
</div>
);
}
}
class Layout extends React.Component {
render() {
return <div>{this.app.state.text}</div>
}
}
When you click on the parent div, this.hello will be called, but the child component will not detect the state update, and may not re-render as expected. If it does re-render, it will be because the parent did. Relying on this will cause future bugs.
A safer pattern is to pass only what is needed into props:
class App extends React.Component {
//...
render() {
return (
<div onClick={this.hello}>
<Layout text={this.state.text}>
</div>
);
}
}
class Layout extends React.Component {
render() {
return <div>{this.props.text}</div>
}
}
This will update as expected.
Answer
There's nothing wrong in passing functions as props, as I can see in your example, the only thing you have to do is make sure your function is bound to the current component like the following example
Reference
React: Passing Functions to Components
Well here I want to use one method to another component, And for that I found a way through composition.
And this is what I did for that
file1.js
import ProductList from '../../views/Products/ProductList';
class CloseableTab extends Component {
constructor() {
super();
this.tpItem = () => {
console.log("hello, item clicked");
};
}
render() {
return (
<div>
<ProductList
itemChange={this.tpItem} />
</div>
);
}
}
export default CloseableTab;
Then in productList I want to call the "tpItem" method by calling itemChange in prop.
Though before that I tried to console the 'prop' of product list. So, it shows me null object in the console. And for that I used the code below:
ProductList.js
export default class ProductList extends Component {
constructor() {
super();
};
render() {
console.log(this.props);
return { }
}
}
So, this gives me null object in the console.
I'll appreciate your help, thanks.
Did you make constructor props enabled ?
Just pass props parameter in constructor
constructor(props) {
super(props)
}
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.
Its not ideal to define functions in the constructor of the component, you can declare them outside of constructor and pass them down, also, in ProductList you are trying to render an object which isn't supported. if you don't want to return anything use return null.
Below code works as expected.
class CloseableTab extends Component {
constructor() {
super();
this.tpItem = () => {
console.log("hello, item clicked");
};
}
render() {
console.log(this.tpItem);
return (
<div>
<ProductList
itemChange={this.tpItem} />
</div>
);
}
}
class ProductList extends Component {
render() {
console.log(this.props);
return null
}
}
However you must write it like
class CloseableTab extends Component {
tpItem = () => {
console.log("hello, item clicked");
};
render() {
console.log(this.tpItem);
return (
<div>
<ProductList
itemChange={this.tpItem} />
</div>
);
}
}
Working sandbox
I have two components as below:
class Parent
{
componentWillMount () {
console.log('parent componentWillMount');
}
}
class Child extends Parent
{
componentWillMount () {
console.log('child componentWillMount');
}
}
But when the child component is loaded the componentWillMount method of the parent component is not loaded automatically. How do you call componentWillMount method of the parent component ?
Thanks
class Child extends Parent
{
componentWillMount () {
super.componentWillMount() ; //parent componentWillMount
console.log('child componentWillMount');
}
}
explain :
Child#componentWillMount overrides Parent#componentWillMount. So :
if you need only logic of Parent#componentWillMount without adding extra-logic, it is recommended to remove componentWillMount from Child .
If you need to call Parent#componentWillMount with appending some logic, retain Child#componentWillMount and call inside it super.componentWillMount();
You can call a parent's method in the follow manner:
class Child extends Perent {
componentWillMount () {
super.componentWillMount();
// Insert your child specific code here
}
}
But as Ben Nyrberg already mentioned in the comments it's not a good practice.
The good practice of reusing components code by following the React way is with components composition:
class Parent extends React.Component {
componentWillMount() {
// Reusable functionality here
}
render() {
return {this.props.children}
}
}
class Child extends React.Component {
componentWillMount() {
// Child specific functionality
}
render() {
return <div>Whatever you want</div>
}
}
class App extends React.Component {
render() {
return <Parent>
<Child />
</Parent>
}
}
When is it important to pass props to super(), and why?
class MyComponent extends React.Component {
constructor(props) {
super(); // or super(props) ?
}
}
There is only one reason when one needs to pass props to super():
When you want to access this.props in constructor.
Passing:
class MyComponent extends React.Component {
constructor(props) {
super(props)
console.log(this.props)
// -> { icon: 'home', … }
}
}
Not passing:
class MyComponent extends React.Component {
constructor(props) {
super()
console.log(this.props)
// -> undefined
// Props parameter is still available
console.log(props)
// -> { icon: 'home', … }
}
render() {
// No difference outside constructor
console.log(this.props)
// -> { icon: 'home', … }
}
}
Note that passing or not passing props to super has no effect on later uses of this.props outside constructor. That is render, shouldComponentUpdate, or event handlers always have access to it.
This is explicitly said in one Sophie Alpert's answer to a similar question.
The documentation—State and Lifecycle, Adding Local State to a Class, point 2—recommends:
Class components should always call the base constructor with props.
However, no reason is provided. We can speculate it is either because of subclassing or for future compatibility.
(Thanks #MattBrowne for the link)
In this example, you are extending the React.Component class, and per the ES2015 spec, a child class constructor cannot make use of this until super() has been called; also, ES2015 class constructors have to call super() if they are subclasses.
class MyComponent extends React.Component {
constructor() {
console.log(this); // Reference Error
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
By contrast:
class MyComponent extends React.Component {
constructor() {
super();
console.log(this); // this logged to console
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
More detail as per this excellent stack overflow answer
You may see examples of components created by extending the React.Component class that do not call super() but you'll notice these don't have a constructor, hence why it is not necessary.
class MyOtherComponent extends React.Component {
render() {
return <div>Hi {this.props.name}</div>;
}
}
One point of confusion I've seen from some developers I've spoken to is that the components that have no constructor and therefore do not call super() anywhere, still have this.props available in the render() method. Remember that this rule and this need to create a this binding for the constructor only applies to the constructor.
When you pass props to super, the props get assigned to this. Take a look at the following scenario:
constructor(props) {
super();
console.log(this.props) //undefined
}
How ever when you do :
constructor(props) {
super(props);
console.log(this.props) //props will get logged.
}
When implementing the constructor() function inside a React component, super() is a requirement. Keep in mind that your MyComponent component is extending or borrowing functionality from the React.Component base class.
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 MyComponent 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 this 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(props).
The entire reason for defining this constructor() funciton is to initialize our state object.
So in order to initialize our state object, underneath the super 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() {
return <div>Hello world</div>;
}
};
So we have defined our constructor() method, initialized our state object by creating a JavaScript object, assigning a property or key/value pair to it, assigning the result of that to this.state. Now of course this is just an example here so I have not really assigned a key/value pair to the state object, its just an empty object.
Dan Abramov wrote an article on this topic:
Why Do We Write super(props)?
And the gist of it is that it's helpful to have a habit of passing it to avoid this scenario, that honestly, I don't see it unlikely to happen:
// Inside React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
// Inside your code
class Button extends React.Component {
constructor(props) {
super(); // 😬 We forgot to pass props
console.log(props); // ✅ {}
console.log(this.props); // 😬 undefined
}
// ...
}
As per source code
function ReactComponent(props, context) {
this.props = props;
this.context = context;
}
you must pass props every time you have props and you don't put them into this.props manually.
super() is used to call the parent constructor.
super(props) would pass props to the parent constructor.
From your example, super(props) would call the React.Component constructor passing in props as the argument.
More information on super:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
For react version 16.6.3, we use super(props) to initialize state element name : this.props.name
constructor(props){
super(props);
}
state = {
name:this.props.name
//otherwise not defined
};
Here we won't get this in the constructor so it will return undefined, but we will be able to fetch this outside the constructor function
class MyComponent extends React.Component {
constructor() {
console.log(this); // Reference Error i.e return undefined
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
If we are using super(), then we can fetch the "this" variable inside the constructor as well
class MyComponent extends React.Component {
constructor() {
super();
console.log(this); // this logged to console
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
So when we are using super(); we will be able to fetch this but this.props will be undefined in the constructor. But other than constructor, this.props will not return undefined.
If we use super(props), then we can use this.props value inside the constructor as well
Sophie Alpert's Answer
If you want to use this.props in the constructor, you need to pass
props to super. Otherwise, it doesn’t matter because React sets .props
on the instance from the outside immediately after calling the
constructor.
Here is the fiddle I've made:jsfiddle.net. It shows that props are assigned not in the constructor by default. As I understand they are assinged in the method React.createElement. Hence super(props) should be called only when the superclass's constructor manually assings props to this.props. If you just extend the React.Component calling super(props) will do nothing with props. Maybe It will be changed in the next versions of React.
I have a button on my main component, when its clicked its open an "Approval pannel", And when the OK is clicked I am calling a callback function to the main component and doing some logic.
I want to pass the callback function(My reasons), The problem is that when the callback function is called, the props and state are undefined.
Why is that happening? Please tell me if any info is missing.
I have added a partial code here:
class MainComponent extends React.Component {
constructor(props){
currentActionConfig = {onOkClick: this.onGenericApprovalOkClicked, ...};
}
onCommandApprovalOkClicked(commandText){
console.log(this.props); <- 'undefined'
}
render(){
return <ActionsApprovalPanel currentActionConfig={this.currentActionConfig}/>
}
}
export default class ActionsApprovalPanel extends React.Component {
render()
{
...
return <ChangeIpApproval onOkClick={this.props.currentActionConfig.onOkClick}/>;
...
}
}
Try these changes
class MainComponent extends React.Component {
constructor(props){
super(props); //1. Call super
this.currentActionConfig = {onOkClick: this.onGenericApprovalOkClicked.bind(this), ...}; // 2.bind this
}
onCommandApprovalOkClicked(commandText){
console.log(this.props); <- 'undefined'
}
render(){
return <ActionsApprovalPanel currentActionConfig={this.currentActionConfig}/>
}
}
export default class ActionsApprovalPanel extends React.Component {
render()
{
...
return <ChangeIpApproval onOkClick={this.props.currentActionConfig.onOkClick}/>;
...
}
}
I think you need to make few changes to your React component.
First: In the constructor call super().
Second:: Define currentActionConfig as a state and try using it as this.state.currentActionConfig
Third: Specify the binding on onCommandApprovalOkClicked(). as
onCommandApprovalOkClicked = (commandText) => {} and similary for other functions.
class MainComponent extends React.Component {
constructor(props){
super(props);
this.state = {
currentActionConfig = {onOkClick: this.onGenericApprovalOkClicked, ...}
};
}
onCommandApprovalOkClicked(commandText){
console.log(this.props); <- 'undefined'
}
render(){
return <ActionsApprovalPanel currentActionConfig={this.state.currentActionConfig}/>
}
}
export default class ActionsApprovalPanel extends React.Component {
render()
{
...
return <ChangeIpApproval onOkClick={this.props.currentActionConfig.onOkClick}/>;
...
}
}
Make these changes and see if they work.