React - props is empty when calling a callback function from child - javascript

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.

Related

React - exporting multiple classes in the same file

I am working on a project in react where I have multiple components. I am trying to do it all in JSBin (lol.. I know). But i am having issues with exporting multiple classes.
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<div></div>
);
}
}
class Bar extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<div></div>
);
}
}
export class Foo{};
export class Bar{};
But I am getting an error of
Parsing error: Identifier 'Foo' has already been declared
92 | }
93 |
> 94 | export class Foo{};
| ^
95 | export class Bar{};
So then I tried to change it to this
class Foo extends React.Component {...my code}
class Bar extends React.Component {...my code}
and it compiles but I get an runtime error of
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
Is it possible to export multiple classes in a single file with react?
It's because you are re-declaring the class at the bottom, you can export your classes like the following.
export class Foo extends React.Component {
// your code
}
export class Bar extends React.Component {
// your code
}
You can declare your classes as usual, then export all your classes at the end of the file as the following
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return <div></div>;
}
}
class Bar extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return <div></div>;
}
}
export { Foo, Bar };
Or if you prefer, you can export the class as you declare them.
export class Foo extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return <div></div>;
}
}
export class Bar extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return <div></div>;
}
}
Generally its suggested that only export one component per file as per es-lint's rule.
You can also do:
export Foo;
export Bar;
in the end instead of declaring the class again.

React, this.props is not defined

I know this is a very common problem and I looked into many other complaints before posting this.
I have class Parent
class Parent extends React.Component {
constructor() {
super();
this.state = {
...
};
}
....
render() {
return (
<MuiThemeProvider theme={materialTheme}>
<Child
ref={...}
groupId={this.state.groupId}
groupUniqueId={this.state.groupUniqueId} />
</MuiThemeProvider>
);
}
}
And a class Child
class Child extends React.Component {
constructor() {
super(props);
this.state = {
...
};
...
}
getUsers() {
const url = `/someurl/${this.props.groupId}`;
...
}
render() {
return (...);
}
}
export default Child;
However, in the class Child, I get an error
"Uncaught ReferenceError: props is not defined"
Is there something obvious that I am missing? Thanks!
This is happening because your this is not referencing a class. It is referring to your function. You can either use arrow functions or bind this to your function in constructor. Just add below line
constructor() {
super(props);
this.state = {
...
}
this.getUsers = this.getUsers.bind(this)
}

Get default state value by receiving prop data - React

I'm new to react.js.
I'd like to get default value of state following_status by receiving props.user.following_status.
I'm passing user object ( user = { following_status: 'following', id:123 } ) to ReactionButton component. ReactionButton component is looks like this:
class RelationButton extends React.Component {
constructor(props){
super(props);
console.log(props.user.following_status) # undefined!!!
this.state = {
following_status: props.user.following_status
}
...
render() {
if (this.state.following_status == 'following') {
<UnFollowBtn/>
} else {
<FollowBtn/>
}
}
RelationButton was called by UserCardHeader component.
const UserCardHeader = (props) => {
const user = props.user;
return(
<header className="user-card--full__header">
<RelationButton user={user}></RelationButton>
</header>
)
}
I don't understand why console.log(props.user.following_status) returns undefined. I googled many websites like those:
React component initialize state from props
accessing props inside react constructor
those answers suggest
class FirstComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
x: props.initialX
};
}
}
but this didn't work for me.
If I add componentWillReceiveProps to the codes above,
componentWillReceiveProps(props){
console.log(props.user.following_status) #=> "following"
this.setState({following_status: props.user.following_status})
}
everything works well. However I think it's weird solution and sometimes doesn't work. Why can't I receive object props in constructor(props) {} section?
Without the full code, we can't tell what's wrong but it is obvious that following_status comes asynchronously to the component and that's why is not accessible right away in the constructor.
To somehow fix it you can detect if props have changed and reset state accordingly in componentDidUpdate.
class RelationButton extends React.Component {
constructor(props){
super(props);
console.log(props.user.following_status) # undefined!!!
this.state = {
following_status: props.user.following_status
}
}
componentDidUpdate(prevProps) {
if(prevProps.user.following_status !== this.props.user.following_status) {
this.setState({ following_status: this.props.user.following_status })
}
}
render() {
// you forgot about return statements :
if (this.state.following_status == 'following') {
return <UnFollowBtn/>
} else {
return <FollowBtn/>
}
}
}

How to use composition in ReactJs

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

componentDidMount() of parent is not called

I have BaseComponent, from which all another components inherit. But if child component has componentDidMount(), parent's componentDidMount() is not called. Is there any way to call componentDidMount() of parent's component always after componentDidMount() of child component? Here is example.
You can use the "super()" function to call the parents implementation.
componentDidMount() {
console.log('Child mounted.');
super();
}
But this is regarded as an anti-pattern. The suggested approach would be composition (details here). Unfortunately, without know what you are trying to accomplish through inheritance, we can't tell you an alternative through composition. In using your example, it can be done something like this
class Animal extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log('Parent mounted.'); // Not working.
}
render() {
return (<div>{this.props.animalType}</div>);
}
}
class Dog extends React.Component {
componentDidMount() {
console.log('Child mounted.');
}
render() {
return (<Animal animalType="Dog" />);
}
}
React.render(<Dog />, document.body);
In your example, your parent component Animal is not actually the parent but is an independent component since anyways you are rendering the Dog component.
This is the Reason that the componentDidMount of Animal Component is not getting called, in fact the Animal component itself is not being rendered but just defined.
In order for Dog to be a child of Animal component, render it from the Parent component(Animal) and change the code like
class Animal extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log('Parent mounted.'); // Not working.
}
render() {
return (
<Dog/>
)
}
}
class Dog extends Animal {
componentDidMount() {
console.log('Child mounted.');
}
render() {
return <div>Dog.</div>;
}
}
React.render(<Animal />, document.body);
JSFIDDLE

Categories

Resources