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
Related
I wanna change the title by clicking the button but it doesn't change, can I have an explanation why is that happens?
import './ExpenseItem.css';
import ExpenseDate from './ExpenseDate';
import Card from './Card';
function ExpenseItem(props){
let title = props.expenseTitle;
function clickedFunc(){
title = "Update!";
}
return(
<Card className='expense-item'>
<ExpenseDate expenseDate={props.expenseDate}></ExpenseDate>
<div className='expense-item__description'>
<h2>{title}</h2>
<div className='expense-item__price'>
₹{props.expenseAmount}
</div>
</div>
<button onClick={clickedFunc}>Click</button>
</Card>
);
}
export default ExpenseItem;
This is not how data is handled with React.
The title should be stored in a state variable (see useState).
Once the data is stored in a state variable, you will have to set it with setState. When setState is called in React, the component holding the state variable re-renders. This will in turn cause your ExpenseItem component to re-render because it is a child component of whatever higher level component passed it props.
In your parent component, you should see something like:
require { useState } from 'react';
const ParentComponent = (props) => {
const [title, setTitle] = useState('Original Title');
...
...
...
return (
<div className="ParentComponent">
<ExpenseItem
title={title}
setTitle={setTitle}
expenseAmount={expenseAmount}
/>
</div>
)
}
Then, in your clickedFunc() function:
function clickedFunc() {
props.setTitle("Update!");
}
Consider this example
export function InsideHoc(props){
const [A, setA] = useState(false);
return({if(A) && (<h1>Print something</h1>)});
}
In another file
import {A, setA} from './inside-file';
function ToggleFromOutside(){
return(<p onClick={setA(!A)}>Verify</p>);
}
Can setA be exposed outside so that the state of this component be changed from outside? I know this can be done through redux. But without using this, is there a way to change the state of one component?
Structure is like this
import {withCreateHOC} from './external';
import childComponent from './child';
class A extends React.Component {
render(){
<Another menus={(item) => <MenuItems object={item} />}
/>
}
}
export default withCreateHOC(A, {custom: childComponent, title: 'Add'});
//withCreateHOC renders modal here as well as it has a button to toggle the state. Same state should be used from below function
function MenuItems(){
return(<button onClick={displayModal}>)
}
Yes.
You can lift the state in that case. This works good if you don't need to pass down the setState to far down the tree.
If you don't want to pass the setState function all the way down the React element tree, you will need to use context, redux or some other state handling.
Example state lift
export function Parent(){
const [message, setMessage] = useState("Hello World");
return (
<>
<Child1 message={message} />
<Child2 changeMessage={setMessage} />
</>
);
}
// Can be in other file
function Child1(props){
return(<p>{props.message}</p>);
}
// Can be in other file
function Child2(props){
return(
<a onClick={() => props.changeMessage("Changed")}>
I can change things in other components.
</a>
);
}
Example of React tree with shared context/redux
<WithRedux>
<App>
<Navigation />
<Modal />
<PageRenderer />
<SomeList>
<ListItem />
<ListItem />
</Somelist>
</App>
<WithRedux>
All the children of the WithRedux component can access the state and modify it.
(PS: You can wrap the App with the HOC withRedux etc, this example is just for visualization)
"The Main class is the container component from where I am importing the Presentational component Menu"
"Regarding dishes.js, DishdetailComponent.js in Main class(where I am importing) these are just used for rendering purpose"
"I just want to know how the onClick() function is communicating with Menu class since there is an onClick()"
"What happens is that there is a list of items displayed on the screen where if an item is clicked the details of it is shown which is handled by another component.. I just want to know what is happening with the onClick() in Menu and the onClick() in Main are they working parallely ? "
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> {/* class name app has been removed */}
<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>
);
}
}
</script>
import React, { Component } from 'react';
import { Card, CardImg, CardImgOverlay, CardText, CardBody, CardTitle } from 'reactstrap';
class Menu extends Component {
constructor(props){
super(props);
}
render(){
const menu=this.props.dishes.map( (dish) => {
return(
<div key={dish.id} className="col-12 col-md-5 m-1">
<Card onClick={() => this.props.onClick(dish.id)}>
<CardImg width="100%" src={dish.image} alt={dish.name} />
<CardImgOverlay body className="ml-4">
<CardTitle>{dish.name}</CardTitle>
</CardImgOverlay>
</Card>
</div>
);
}) ;
return(
<div className="container">
<div className="row">
</div>
</div>
);
}
}
export default Menu;
onClick on Menu component rendered in Main component is not an even handler but a prop passed on to the Menu component. For the sake of understand you can think of it like handleClick
The onClick on Card inside Menu is again a prop passed on to the Card component. Since card ccomponent is used from reactstrap, it internally would be rendering a div wherein it would attach an actual onClick handler
Now when the Card is clicked, the handler inside Card is trigger which then calls the onClick passed on as props to it and it further calls this.props.onClick(dish.id) which is the function passed on from Menu component.
The onClicks functions are not executed parallelly but sequentially communication information from child to the parent and further to the grandparent.
The problem is with your naming... It confuses you. The onClick attribute on your Menu is a property passed to your react Menu component. The this.props.onClick receives the function defined in the onClick attribute.
In your menu, the onClick attribute is set on a HTML element so that defines a click handler. It happens that your click handler executes the function in this.props.onClick which has been passed from the parent.
I suggest to change the property name to something more explicit, something like onDishSelection maybe?
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);
I'm building a webpage and realized a common style shared by each component (same background, border, and title style). So I thought I should make an HOC which accepts the inner content of each component as well as a title, and returns an outer component which wraps this inner component and heading.
At first I ran into a lot of issues trying to get this to work, being new to React, but now it's finally working but I still don't understand how.
Here is my HOC
const BaseBlock = (WrappedComponent) => {
return class BaseBlock extends Component {
render () {
return (
<div className={styles['base-block']}>
<div className={styles['container']}>
<div className={styles['base-block-head']}>
{ this.props.title }
</div>
<div className={styles['base-block-body']}>
<WrappedComponent {...this.props} />
</div>
</div>
</div>
)
}
}
}
export default BaseBlock
This is the WrappedComponent:
const HighlightsBlock = (props) => {
return <ListsComponent items={props.items} />
}
export default BaseBlock(HighlightsBlock)
And this is the ListsComponent
const ListsComponent = (props) => {
if (props.items) {
return (
<ul className={styles['styled-list']}>
{props.items.map((item, idx) => {
return (
<li key={idx} className={styles['styled-list-item']}>{item}</li>
)
})}
</ul>
)
} else return (
<h3>No highlights</h3>
)
}
export default ListsComponent
And this is how I'm using the component in my app:
<HighlightsBlock items={this.getHighlights()} title='Highlights' />
Now, I can see the HighlightsBlock component receiving props twice (Once when I'm using it in my App with props, and once inside the HOC Baseblock as WrappedComponent ). If I remove props from either of these places it stops working. I don't understand how this is working.
When you render <HighlightsBlock items={this.getHighlights()} title='Highlights' /> you are actually rendering the component returned by HOC which in turn renders your actually HighlightsBlock component as <WrappedComponent {...this.props} />
You can think of HighlightsBlock component to be nested two level deep and hence you need to pass on the props to it, firstly as {...this.props} from within HOC and then receive it as props in functional component
This is because of this.getHighlights() in this line,
<HighlightsBlock items={this.getHighlights()} title='Highlights' />
Every time you pass props to child component this function is getting executed.
To solve this issue, maintain a state value in your parent component and set that value in getHighlights function like,
getHighlights(){
//you logic to get data
this.setState({items:data.items}); //considering `data` is object which has `items`
}
Now you can pass items like,
<HighlightsBlock items={this.state.items} title='Highlights' />