I am new to react(started this week). I have a parent and child components and I want to call a parent method in the child component. I have searched through stackoverflow and my code is same as all the solutions I got.
I have a parent component ProductDisplay which displays a list of products:
import React, { Component } from 'react';
import data from '../data'
import Product from '../Product/product.component'
class ProductDisplay extends Component {
constructor(props) {
super(props)
this.state = {
pdts: data,
}
}
addToCart = () => {
console.log('add to cart');
}
render() {
return (
this.state.pdts.map(product => (
<Product
key={product.id}
product={product}
addToCart={this.addToCart}
/>
))
);
}
}
export default ProductDisplay;
and the child component is Product which renders each product
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import "./product.css";
class Product extends Component {
constructor(props) {
super(props);
}
handleClick = () => {
this.props.addToCart();
console.log('click');
}
render() {
const product = this.props.product;
console.log(this.props.addToCart);
return (
<div className="product">
<img className="image" src={product.imgPath} alt={product.name} />
<p className="name">{product.name}</p>
<p className="price">Price: ₹{product.price}</p>
<p className="category">Category: {product.category}</p>
<button className="add">Add To Cart <i className="fa fa-cart-plus"
onClick={this.handleClick}></i></button>
</div>
);
}
}
export default withRouter(Product);
I want to call a function addToCart of ProductDisplay from Product on click of the button but it is not working. The handleClick function of the child omponent itself is not getting called. Hence the parent function which is being called from handleClick is also not getting called.
I’m also not sure if what I am doing would work in binding the method to all the buttons. Please help
You've put the onClick listener on the <i> tag, not on the actual button, which is why its not triggering anything when you click the button.
Try this instead:
<button
className="add"
onClick={this.handleClick}
>
Add To Cart <i className="fa fa-cart-plus"></i>
</button>
You need to bind the addCart method with "this" of the class. And as Chistopher's answer your onClick is on i and not on button.
Either while passing.
<Product
key={product.id}
product={product}
addToCart={this.addToCart}
/>
Or in state
this.addToCart = this.addToCart.bind(this);
Related
I have a Parent (Class Based Component) and a Child (Function Based Component). I need the method of function based component, when I will click on (+) Button of Parent Class Component.
I have imported child component to parent component
I can't handle the click event in here of parent component
onClick={ }
Here is my Parent Class Based Component
import React from "react";
import Increment from "./Increment";
class Timer extends React.Component {
//State
state = {
count: 0
}
render() {
return (
<div className="top">
<span className="display">{this.state.count}</span>
<button className="btn btn-primary" onClick={<Increment />} >
+
</button>
</div>
)
}
}
export default Timer
Here is my Child Function Component
import React, { Component } from "react";
class Increment extends Component {
incrementTimer = props => {
this.setState({ count: props.count + 1 })
}
}
export default Increment
I believe this article may shed some light on your issue: Lifting State Up - React Docs. I wouldn't suggest trying to update the state of Component #1 from inside Component #2.
Take the increment logic from your Increment Component and make it a method on your Parent Component. Then pass the method as a prop on the Increment Component.
In Timer.js
import React, { Component } from "react";
import Increment from "./Increment";
class Timer extends Component {
// The Constructor is necessary for adding the handleIncrement method
// State should be initialized here as well.
constructor(props) {
// super(props) is required on class based component constructors
super(props);
this.state = { count: 0 };
this.handleIncrement = this.handleIncrement.bind(this);
}
// This is the method
handleIncrement() {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div className="top">
<span className="display">{this.state.count}</span>
{/* handleIncrement is the name of the prop that will be referenced inside
the Increment.js Component. */}
{/* this.handleIncrement is the method. */}
<Increment handleIncrement={this.handleIncrement} />
</div>
);
}
}
export default Timer;
In Increment.js
import React from "react";
// Putting (props) means this component is expecting a prop when
// its been imported and used as <Increment propGoesHere={value} /> in Timer.js
const Increment = (props) => {
return (
<button className="btn btn-primary" onClick={props.handleIncrement}>
+
</button>
);
};
export default Increment;
As an aside, Class based components can be avoided altogether (because who wants to deal with the "this" keyword, constructors, and binding every method) by using the useState hook.
It would look something like:
In Timer.js
import React, { useState } from "react";
const Timer = () => {
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount(count + 1);
};
return (
<div className="top">
<span className="display">{count}</span>
<button className="btn btn-primary" onClick={handleIncrement}>
+
</button>
</div>
);
};
export default Timer;
No need for a class to hold the state anymore, now any component can house state!
No more constructor, no need to bind methods, no more "this" keyword!
No need for the entire Incremement child Component
First of all
<button className="btn btn-primary" onClick={<Increment />} >
+
</button>
This specific piece of code is impossible to make work. You cannot pass a component as a function to an onClick.
Since I do not know the practical application of your question I have no clue what is exactly you are trying to achieve so here is just an option of how to make these specific components work.
import React from "react";
import Increment from "../components/Increment";
class Timer extends React.Component {
//State
state = {
count: 0,
mountCounter: false,
};
increaseCount = () => {
this.setState({ count: this.state.count + 1, mountCounter: false });
};
render() {
return (
<div className="top">
<span className="display">{this.state.count}</span>
<button
className="btn btn-primary"
onClick={() => this.setState({ mountCounter: true })}
>
+
{this.state.mountCounter && (
<Increment increaseCount={() => this.increaseCount()} />
)}
</button>
</div>
);
}
}
export default Timer;
import React, { Component } from "react";
class Increment extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.increaseCount();
}
render() {
return null;
}
}
export default Increment;
Parent Component is like below
import React, { useState } from "react";
import EntityDefinition from "./EntityDefinition";
export default function EntitySelection(props) {
const testFun = () => {
console.log("Function activated");
};
return (
<>
<div>
<EntityDefinition
testFun={testFun}
/>{/**Calling a Class based Component*/}
</div>
</>
);
}
Class based Component (Child)
import React from "react";
import { ComboBox, DropdownOption, Button } from "react-widgets";
import axios from "axios";
export default class EntityDefinition extends React.Component {
render() {
return (
<React.Fragment>
<div>
{" "}
<Button onClick={this.testFun}>Close</Button>{" "} {/*/Calling the function passed*/}
</div>
</React.Fragment>
);
}
}
but when i clicked the button the testFun function is not being called.
i tried something like onClick={this.state.testFun} , onClick={testFun}
but nothing is happening.
can someone point what am i doing wrong here.
testFun is a prop. So use this.props.testFun
onClick={this.props.testFun}
testFun is a prop, and you are using the ES6 class component, and it receives the props as a props object, so you can try accessing it as below
onClick={this.props.testFun}
You need to refer to props passed to a class based components using this.props from inside the class based components:
Docs
In your case, you should change the onClick listener to this:
<Button onClick={this.props.testFun}>
So what I have is two components - one is MainComponent and the other is MenuComponent written in ReactJS
The MenuComponent is imported into the MainComponent
The MainComponent is as shown below:
import React, { Component } from 'react';
import { Navbar, NavbarBrand } from 'reactstrap';
import Menu from './MenuComponent';
import DishDetail from './DishdetailComponent';
import { DISHES } from '../shared/dishes';
class Main extends Component{
constructor(props){
super(props);
this.state={
dishes: DISHES,
selectedDish: null
}
}
onDishSelect(dishId){
this.setState({selectedDish: dishId})
}
render(){
return(
<div className="App">
<Navbar dark color="primary">
<div className="container">
<NavbarBrand href="/">Ristorante Con Fusion</NavbarBrand>
</div>
</Navbar>
<Menu dishes={this.state.dishes} onClick={(dishId)=>this.onDishSelect(dishId)}/>
<DishDetail dish={this.state.dishes.filter((dish)=>dish.id===this.state.selectedDish)[0]}/>
</div>
)
}
}
export default Main;
The render method of MenuComponent is as shown below:
render(){
const menu=this.props.dishes.map((dish) => {
return(
<div className="col-12 col-md-5 m-1">
<Card key={dish.id}
onClick={() => this.props.onClick(dish.id)}>
<CardImg width="100%" src={dish.image} alt={dish.name} />
<CardImgOverlay>
<CardTitle><h5 className='font'>{dish.name}</h5></CardTitle>
</CardImgOverlay>
</Card>
</div>
);
});
I'm totally new to React, so I would like to know how the onClick event handler defined in MenuComponent works:
So when a card is clicked (in MenuComponent), is the this.props.onClick invoked? Why
is this.props used here?
The same onClick method is used in the MainComponent, with a dishId
(from where?) passed as parameter, I'm kind of confused on how this
all onClick event is processed
You're using this.props to update the state in the parent component. Without using this.props, there is no way to update state in the parent component with your child component. This is how you update state with a child component.
When you import a component into another component, that imported component becomes that components child.
Yes when a card is clicked, it invokes the onClick prop you have created.
You are right.
The dish.id is passed to props.onClick when click occurs on a card. and
props.onClick is onDishSelect actually, when called with an argument, it will set parent component's state.selectedDish equal to that argument(in this case child's dish id)
Here You are passing onClick method as props to Menu component with argument
<Menu dishes={this.state.dishes} onClick={(dishId)=>this.onDishSelect(dishId)}/>
Here you are calling that onClick props inside Menu component when card is being clicked passing dishId as argument
<Card key={dish.id}
onClick={() => this.props.onClick(dish.id)}>
<CardImg width="100%" src={dish.image} alt={dish.name} />
<CardImgOverlay>
<CardTitle><h5 className='font'>{dish.name}</h5></CardTitle>
</CardImgOverlay>
</Card>
When you are clicking on the <Card> element, this fires the onClick function that is received by MenuComponent via props. So basically this.props is an object representing props passed to the component and from that object you're extracting your onClick which in case of implementation of this component in MainComponent is your onDishSelect method.
You could as well invoke the MenuComponent in different context, and could pass different function as the onClick prop.
const SomeOtherComponent = () => {
const logHelloOnClick = () => {
console.log('hello');
}
return (
<MenuComponent onClick={logHelloOnClick}/>
)
}
In this case when you clicked on the Card, "hello" msg would be logged to the console.
Therefore try to realize that props passed to a component are basically the same as arguments passed to any other function.
const logTestToConsole = text => {
console.log(text)
};
const functionMimickingComponent = props => {
const taskToRun = props.tastToRun;
taskToRun('hello');
};
functionMimickingComponent({ taskToRun: logTestToConsole }); // this logs "hello" to the console
I created a reset function in App.js and want to call it by an onclick in two other components. the problem is that it works in one component but doesn't in the other.
Here are the codes snippets
App.js
import React from 'react';
import Result from './components/Result';
import GeneralResult from './components/GeneralResult';
class App extends Component {
constructor(props) {
super(props);
this.state = {
result: '',
counter: 0,
}
}
// Reset function
handleReset=()=>{
this.setState({
result: '',
counter: 0,
)}
renderResult() {
return (
<div>
<Result reset={()=>this.handleReset()} />
<GeneralResult back={()=>this.handleReset()} />
</div>
);
}
Result.js
first component making use of reset()
function Result(props) {
return (
<div>
<span>
<button onClick={props.reset}>Replay</button>
</span>
</div>
);
}
export default Result;
GeneralResult.js
second component making use of the reset
import React, { Component } from 'react';
export default class GeneralResult extends Component {
render() {
return (
<React.Fragment>
<h2>Congratulations you won!</h2>
<span>
<button onClick={props.back}> Back to Question</button>
</span>
</React.Fragment>
);
}
}
You can pass the handler as props, and render the component from the parent class.
class Child extends Component {
render(){
return(
<button onClick = {this.props.onClick}></button>
)
}
}
export default Child;
import Child from 'path/to/child';
class Parent extends Component {
onClick = (e) => {
//do something
}
render () {
return(
<Child onClick = {onCLick}/>
)
}
}
Problem is that GeneralResult is class based component. so when you need to access props passed to it. you have to use this.props.
export default class GeneralResult extends Component {
render() {
return (
<React.Fragment>
<h2>Congratulations you won!</h2>
<span>
// you need to change "props.back"
// to "this.props.back"
<button onClick={this.props.back}> Back to Question</button>
</span>
</React.Fragment>
);
}
}
I come from vue.js, so I'm still a bit confused about react.
I have a Message component, that shows up flash messages:
import React, {Component} from "react";
export default class Message extends Component {
constructor(props) {
super(props);
this.state = {messages: []};
}
setMessage(type, body) {
this.setState({messages: this.state.messages.concat({type: type, body: body})})
setTimeout(() => {
this.removeMessage()
}, 10000);
}
removeMessage() {
let messages = this.state.messages;
messages.shift();
this.setState({messages: messages});
}
render() {
return (
<div className="uk-messages">
{
this.state.messages.map((message, index) => {
if (message.type === 'error') {
message.type = 'danger';
}
return (
<div className={'uk-alert-' + message.type} data-uk-alert key={index}>
<p>{message.body}</p>
</div>
)
})
}
</div>
)
}
}
I use this component in my Index component:
import React, {Component} from 'react'
import ReactDOM from 'react-dom'
import {BrowserRouter as Router, Link, Route, Redirect} from 'react-router-dom'
import Auth from './helpers/auth'
import Message from './helpers/message'
class Index extends Component {
constructor(props) {
super(props);
this.state = {
authState: Auth.state,
};
Auth.initialize();
}
render() {
return (
<Router>
<div className="uk-flex uk-flex-column">
<nav className="uk-navbar-container" data-uk-navbar>
<div className="uk-navbar-left">
<a className="uk-navbar-item uk-logo" href="#"><img src={'images/logo.png'}/></a>
<ul className="uk-navbar-nav">
<li><Link to="/">Home</Link></li>
</ul>
</div>
<div className="uk-navbar-right">
<ul className="uk-navbar-nav">
<li>
<a href="#" className="uk-open" aria-expanded="true">
<span data-uk-icon="icon: user" />
</a>
</li>
</ul>
</div>
</nav>
<div className="uk-flex-grow-1">
<Route path="/" component={Welcome} />
</div>
<footer>
© 2018 stackoverflow.com
</footer>
<Message />
</div>
</Router>
);
}
}
if (document.getElementById('root')) {
ReactDOM.render(<Index/>, document.getElementById('root'));
}
Ok, so the Index component is the one I start with. I import the Message component and render it. That works. In chrome react console, I select the Message tag and can add some messages with
$r.setMessage('success','Greetings my old friend!')
The message appears. So now, how can I use the method setMessage in my Index component? In vue.js it's so simple (use $parent or $children)...
The most direct way to access the methods of another component is to pass refs, but you should probably approach the issue in a different way. Once you start tying your components together with refs, you make them less reusable and more difficult to work with.
If the list of messages were stored at a higher level, and passed into your Message component as a prop, then you would have no need for refs.
So I would recommend moving messages into the state of the Index component (or possibly even to a store), then passing messages as a prop to <Message messages={this.state.messages} />.
If you also want to manipulate the messages from within the Message component, then also pass whatever callbacks from <Index> that you need.
Below is a toy example showing the general idea of storing the state in the parent component and passing messages and addMessage as props to <Message>.
const Message = ({ messages, addMessage }) => (
<div>
{messages.map(({ text, id }) => <div key={id}>{text}</div>)}
<button onClick={addMessage}>add</button>
</div>
);
class Index extends React.Component {
constructor(props) {
super(props);
this.addMessage = this.addMessage.bind(this);
}
state = {
messages: [],
id: 0
};
addMessage() {
this.setState({
messages: [...this.state.messages, { text: "new message", id: this.state.id }],
id: this.state.id + 1
});
}
render() {
return <Message messages={this.state.messages} addMessage={this.addMessage} />
}
}
ReactDOM.render(<Index/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
You can use refs to get the reference to the element e.g.
<Message refs={(ref) => this.messageElement = ref }/>
With that you can call methods on it from anywhere after it has been assigned
this.messageElement.setMessage();
You can use refs (references) as
<Message ref={(c) => this.messageComponent = c} />
and make functions of Message component with context bound so that it is accessible in other components like
Message Component
constructor(props) {
super(props);
this.state = {messages: []};
this.setMessage = this.setMessage.bind(this);
}
Usage in Index component
this.messageComponent.setMessage('type','body');
Read more of refs here