I want to pass a function as a props to my material ui function.
The given function is undefined in my material ui fonction.
import React, { Component } from 'react';
import styled from 'styled-components';
import InputBase from '#material-ui/core/InputBase';
import IconButton from '#material-ui/core/IconButton';
import Menu from '#material-ui/core/Menu';
import MenuItem from '#material-ui/core/MenuItem';
import SearchIcon from '#material-ui/icons/Search';
import Avatar from '#material-ui/core/Avatar';
import '../../css/App.css';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { withTranslation } from 'react-i18next';
import CreateNewGarden from './CreateNewGarden';
class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
};
this.myFunction = this.myFunction.bind(this);
}
myFunction() {
console.log("OK")
}
render() {
return (
<div>
<CreateNewGarden myFunction={this.myFunction}/>
</div>
);
}
}
const mapStateToProps = (state) => ({
});
Dashboard.propTypes = {
};
export default withTranslation()(withRouter(connect(mapStateToProps)(Dashboard)));
I send CreateNewGarden myFunction={this.myFunction} as a props and in my others file.
I have:
import React from 'react';
import { withStyles } from '#material-ui/core/styles';
import Dialog from '#material-ui/core/Dialog';
import MuiDialogTitle from '#material-ui/core/DialogTitle';
import MuiDialogContent from '#material-ui/core/DialogContent';
import MuiDialogActions from '#material-ui/core/DialogActions';
import IconButton from '#material-ui/core/IconButton';
import CloseIcon from '#material-ui/icons/Close';
import Typography from '#material-ui/core/Typography';
import Slider from '#material-ui/core/Slider';
import { useTranslation } from 'react-i18next';
import measureLogo from '../../assets/images/measure.png';
import { Button } from '../../components';
const styles = (theme) => ({
root: {
margin: 0,
padding: theme.spacing(2),
},
closeButton: {
position: 'absolute',
right: theme.spacing(1),
top: theme.spacing(1),
color: theme.palette.grey[500],
}
});
const DialogTitle = withStyles(styles)((props) => {
const {
children, classes, onClose, ...other
} = props;
return (
<MuiDialogTitle disableTypography className={classes.root} {...other}>
<Typography variant="h6">{children}</Typography>
{onClose ? (
<IconButton aria-label="close" className={classes.closeButton} onClick={onClose}>
<CloseIcon />
</IconButton>
) : null}
</MuiDialogTitle>
);
});
const DialogContent = withStyles((theme) => ({
root: {
padding: theme.spacing(2)
}
}))(MuiDialogContent);
const DialogActions = withStyles((theme) => ({
root: {
margin: 0,
padding: theme.spacing(1)
}
}))(MuiDialogActions);
export default function CustomizedDialogs(props) {
const [open, setOpen] = React.useState(false);
// eslint-disable-next-line no-unused-vars
const [height, setHeight] = React.useState(0);
// eslint-disable-next-line no-unused-vars
const [width, setWidth] = React.useState(0);
console.log("ici = " + props.myFunction)
const setSizeHeight = () => (e, value) => {
setHeight(value);
};
const setSizeWidth = () => (e, value) => {
setWidth(value);
};
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const { t } = useTranslation();
return (
<div className="marginCardComponent">
<Button
onClick={handleClickOpen}
text="dashboard.createGardenBtn"
type="submit"
/>
<Dialog onClose={handleClose} aria-labelledby="customized-dialog-title" open={open}>
<DialogTitle id="customized-dialog-title" className="centerText" onClose={handleClose}>
{t('dashboard.createGardenTitle')}
</DialogTitle>
<DialogContent className="logoMeasureParent">
<img src={measureLogo} alt="Logo" className="logoMeasure centerText" />
</DialogContent>
<DialogContent dividers>
<Typography className="centerText" gutterBottom>
{ t('dashboard.createGardenDetail') }
</Typography>
</DialogContent>
<div className="marginLeft3">
<p>{ t('dashboard.height') }</p>
</div>
<div className="centerSlider">
<Slider
/* eslint-disable-next-line react/destructuring-assignment */
defaultValue={0}
aria-labelledby="discrete-slider"
valueLabelDisplay="auto"
step={1}
marks
min={1}
max={20}
onChange={setSizeHeight()}
/>
</div>
<div className="marginLeft3">
<p>{ t('dashboard.width') }</p>
</div>
<div className="centerSlider">
<Slider
/* eslint-disable-next-line react/destructuring-assignment */
defaultValue={0}
aria-labelledby="discrete-slider"
valueLabelDisplay="auto"
step={1}
marks
min={1}
max={20}
onChange={setSizeWidth()}
/>
</div>
<DialogActions>
<Button
onClick={handleClose}
text="dashboard.cancelBtn"
type="submit"
/>
<Button
onClick={props.myFunction}
text="dashboard.createGardenBtn"
type="submit"
/>
</DialogActions>
</Dialog>
</div>
);
}
When i click on the button it does nothing and when i print myFunction it tell me undefined.
Why i can't give a props to the function and call myFunction ?
Thank you for your help.
You need to call it outside of onClick. Do it like this:
const handleClick = (e) => {
e.preventDefault()
props.myFunction()
}
And in the button:
<Button
onClick={handleClick}
text="dashboard.createGardenBtn"
type="submit"
/>
That will work. It just isnt letting you call it inside onClick
You can also just do this:
<Button
onClick={() => props.myFunction()}
text="dashboard.createGardenBtn"
type="submit"
/>
Related
Here it's written header.js code and it's testing with jest framework. While im testing im getting the error property of match of props.value as it is undefined. When i compile , compiler shows this link to solve error but its not getting.
[Link:]https://reactjs.org/link/error-boundaries
Header.js
import React from "react";
import { AppBar, Typography, Toolbar, Tabs, Tab, Button } from "#mui/material";
import {IconButton} from "#mui/material";
import { withStyles } from "#material-ui/core/styles";
import {Logout} from '#mui/icons-material'
import "./Header.css";
import Profile from '../Profile/Profile';
import Wallet from '../Wallet/Wallet';
import Market from "../Market/Market";
import Portfolio from "../Portfolio/Portfolio";
import Trade from "../Trade/Trade";
const Header = (props) => {
const { match, history } = props.value;
const { params } = match;
const { page } = params;
console.log(page);
const IndexToTableName = {
0:"Profile",
1:"Walllet",
2:"Trade",
3:"Market",
4:"Portfolio"
};
const TabNametoIndex = {
Profile: 0,
Wallet: 1,
Trade:2,
Market:3,
Portfolio:4
};
let[value,setValue]=React.useState(TabNametoIndex[page]);
const handleChange=(event,newValue)=>{
history.push(`/UserHome/${IndexToTableName[newValue]}`);
setValue(newValue);
};
const WhiteTextTypography = withStyles({
root: {
color: "#25316D"
}
})(Typography);
return (
<div id="Header" style={{height:"100vh"}} data-testid="header">
<AppBar style={{ background: "#7FBCD2", marginleft: "auto", fontFamily:"sans-serif" }} position="sticky">
<Toolbar>
<WhiteTextTypography variant="h5" id="Typo">TradeHut</WhiteTextTypography>
<Tabs id="Tabs" textColor="white" indicatorColor="primary" value={value} onChange={handleChange}>
<Tab label="Profile" className="tab"/>
<Tab label="Wallet" className="tab"/>
<Tab label="Trade" className="tab"/>
<Tab label="Market" className="tab"/>
<Tab label="Portfolio" className="tab"/>
</Tabs>
<div id="IconButton">
<IconButton color="primary" aria-label="add an alarm" size="large" id="logout">
<Logout/>
</IconButton>
</div>
</Toolbar>
</AppBar>
{value===0 && <Profile/>}
{value===1 && <Wallet/>}
{value===2 && <Trade/>}
{value===3 && <Market/>}
{value===4 && <Portfolio/>}
</div>
)
}
export default Header;
Header.test.js
import React from 'react';
import Header from "./Header";
import { render, screen } from '#testing-library/react';
import '#testing-library/jest-dom'
describe("Test the Header Component", () => {
test("To Check whether Header Div is present or not", () => {
render(<Header />);
const buttonList = screen.getByTestId("header");
expect(buttonList).toBeTruthy();
});
});
const Header = (props) => {
const { match, history } = props.value;
...
}
In your test you call render(<Header/>) but do not pass any props to the component. You need to provide Header with the required props.
render(<Header value={whateverValueShouldBe} />)
I am having two components, App and a panel. On button clcik, I add panel to the screen and all the actions corresponding actions inside of the panel is handled in the Panel component ( Actions are expand, collapse and close). Can I execute onClose method inside of the Panel component instead in App component. I am having a check of if cardBody is present in the parent component. For removing that card im setting its body to null inside App component. How can I do the same in Panel component.
https://codesandbox.io/s/basic-demo-card-6ywop7?file=/src/Panel.jsx:0-1022
import React, { useState } from "react";
import ReactDOM from "react-dom";
import Panel from "./Panel";
import "./styles.css";
function App() {
const [card, setCard] = useState({
cardId: "",
cardBody: null
});
const handleClick = (cardId, cardBody) => {
setCard({ cardId, cardBody });
};
const { cardId, cardBody } = card;
return (
<>
<div className="main">
<button onClick={() => handleClick("Panel 1", <h1>h1</h1>)}>
Add Panel 1
</button>
<button onClick={() => handleClick("Panel 2", <div>div</div>)}>
Add Panel 2
</button>
</div>
{cardBody && (
<div className="cards-container">
<Panel
key={cardId}
cardId={cardId}
cardBody={cardBody}
onClose={() =>
setCard({
cardId: "",
cardBody: null
})
}
/>
</div>
)}
</>
);
}
import React, { useState, useCallback } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {
faSquareMinus,
faRectangleXmark
} from "#fortawesome/free-solid-svg-icons";
function Panel(props) {
const [isMinimized, setIsMinimized] = useState(false);
const { cardId, cardBody, onClose } = props;
const onMaximize = useCallback(() => {
setIsMinimized(!isMinimized);
}, [isMinimized]);
return (
<>
<div className={isMinimized ? "card-min" : "card"}>
<div className="card-actions">
<span onClick={onMaximize}>{cardId}</span>
{!isMinimized && (
<FontAwesomeIcon
icon={faSquareMinus}
onClick={() => {
setIsMinimized(true);
}}
/>
)}
<FontAwesomeIcon icon={faRectangleXmark} onClick={onClose} />
</div>
<div className="card-body">{cardBody}</div>
</div>
</>
);
}
export default Panel;
One workaround is to control the show/hide by display: 'none'/'block' inside the Panel component.
Panel component
import React, { useImperativeHandle, useState, useCallback } from
"react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {
faSquareMinus,
faRectangleXmark
} from "#fortawesome/free-solid-svg-icons";
function Panel(props) {
const [isMinimized, setIsMinimized] = useState(false);
const { cardId, cardBody, setCard } = props;
const onMaximize = useCallback(() => {
setIsMinimized(!isMinimized);
}, [isMinimized]);
return (
<>
<div
style={{ display: cardBody ? "block" : "none" }}
className={isMinimized ? "card-min" : "card"}
>
<div className="card-actions">
<span onClick={onMaximize}>{cardId}</span>
{!isMinimized && (
<FontAwesomeIcon
icon={faSquareMinus}
onClick={() => {
setIsMinimized(true);
}}
/>
)}
<FontAwesomeIcon
icon={faRectangleXmark}
onClick={() =>
setCard({
cardId: "",
cardBody: null
})
}
/>
</div>
<div className="card-body">{cardBody}</div>
</div>
</>
);
}
export default Panel;
App.js
import React, { useState, useRef } from "react";
import ReactDOM from "react-dom";
import Panel from "./Panel";
import "./styles.css";
function App() {
const [card, setCard] = useState({
cardId: "",
cardBody: null
});
const handleClick = (cardId, cardBody) => {
setCard({ cardId, cardBody });
};
const { cardId, cardBody } = card;
return (
<>
<div className="main">
<button onClick={() => handleClick("Panel 1", <h1>h1</h1>)}>
Add Panel 1
</button>
<button onClick={() => handleClick("Panel 2", <div>div</div>)}>
Add Panel 2
</button>
</div>
<div className="cards-container">
<Panel
key={cardId}
cardId={cardId}
cardBody={cardBody}
setCard={setCard}
/>
</div>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
This is working example
Also, you can completely conditionally show and hide the inner content of the panel but you need some modifications in the style by moving the "card-min" and "card" class to an inner div instead of the root element on the component.
I made a react/redux application using firebase, but when i open the modal and close it or add a product all data of application are set hidden
App.js file
import React, { useState } from "react";
import "./App.css";
import Layout from "./components/Layout/Layout";
import Header from "./components/Header/Header";
import FormModal from "./components/Modal/FormModal";
import Modal from "./components/Modal/Modal";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";
import ProductReducer from "./reducers/ProductReaducer";
import Card from "./components/Card/Card";
function App() {
const store = createStore(ProductReducer);
const [showModal, setShowModal] = useState(false);
const onShowModal = () => {
setShowModal(true);
};
const onHideModal = () => {
setShowModal(false);
};
return (
<div>
<Provider store={store}>
<Header onShowModal={onShowModal} />
<Layout>
<Card />
</Layout>
<Modal show={showModal}>
<FormModal show={showModal} onHideModal={onHideModal} />
</Modal>
</Provider>
</div>
);
}
export default App;
Modal.js file
import React from "react";
import { useSelector } from "react-redux";
const Modal = (props) => {
const states = useSelector((state) => state);
console.log(states);
return (
<div
className={
props.show
? `bg-slate-400 fixed top-0 left-0 w-screen h-screen flex justify-center items-center z-1`
: "hidden"
}
>
{props.children}
</div>
);
};
export default Modal;
FormModal.js file
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { addItem } from "../../actions/ProductActions";
import { collection, addDoc, getFirestore } from "firebase/firestore";
const FormModal = (props) => {
const [infoProduct, setInfoProduct] = useState({
title: "",
url: "",
description: "",
price: 0,
});
const handleChange = (evt) => {
const targetValue = evt.target.value;
const key = evt.target.name;
setInfoProduct((old) => ({
...old,
[key]: targetValue,
}));
};
const dispatch = useDispatch();
const db = getFirestore();
const addProduct = async () => {
try {
const docRef = await addDoc(collection(db, "webjump-store"), productItem);
dispatch(addItem(infoProduct));
console.log("Document written with ID: ", docRef.id);
} catch (e) {
console.error("Error adding document: ", e);
}
};
const productItem = {
produto: {
title: infoProduct.title,
url: infoProduct.url,
description: infoProduct.description,
price: infoProduct.price,
},
};
return (
<div className={props.show ? "bg-slate-300 w-64 p-6 z-1" : "hidden"}>
{" "}
<button type="button" className="float-right" onClick={props.onHideModal}>
X
</button>
<form className="flex flex-col items-center">
<label>Title</label>
<input
className="my-3"
type="text"
name="title"
onChange={handleChange}
/>
<label>Url</label>
<input
className="my-3"
type="text"
name="url"
onChange={handleChange}
/>
<label>Description</label>
<input
className="my-3"
type="text"
name="description"
onChange={handleChange}
/>
<label>Price</label>
<input
className="my-3"
type="text"
name="price"
onChange={handleChange}
/>
<button
className="my-4 p-4 bg-slate-200 border-2 border-solid border-black"
type="button"
onClick={addProduct}
>
Add Product
</button>
</form>
</div>
);
};
export default FormModal;
I have no ideia o it can be.
When i do a console.log in my redux state it appers to be empty.
But when i reload the page all data from fire store appears again.
I'm trying to use the useContext hook in my react app to manage the cart state but I don't know why am I getting this error again and again that the function that I'm trying to destructure is undefined.
I'm new to using context API please help me with the same. I saw other answers related to context API and tried everything like exporting the CartContext as default and then importing it without destructoring but nothing worked. I'm neither able to import the functions nor the data.
Here is my CartContext.js file
import React, { useState, createContext, useReducer } from "react";
import CartReducer from "./CartReducer";
//Initialized Context
export const CartContext = createContext();
export const CartProvider = ({ children }) => {
const [products, setProducts] = useState([]);
const [state, dispatch] = useReducer(CartReducer, products);
function addProductWithQuantity(product) {
dispatch({
type: "ADD_PRODUCT",
payload: {
product,
quantity: 1,
},
});
}
const deleteProductWithId = (id) => {
dispatch({
type: "DELETE_PRODUCT",
payload: id,
});
};
return (
<CartContext.Provider
value={{
state,
addProductWithQuantity,
deleteProductWithId,
}}
>
{children}
</CartContext.Provider>
);
};
And here is the file where I'm using the useContext.
import React, { useContext } from "react";
import {
Card,
Typography,
Tooltip,
IconButton,
Button,
} from "#material-ui/core";
import { Link } from "react-router-dom";
import styles from "./Product.module.css";
import amazon from "../../icons/amazon-brands.png";
import flipkart from "../../icons/flipkart.png";
import { SnackbarProvider, useSnackbar } from "notistack";
import classNames from "classnames";
import { CartContext } from "../../Context/CartContext";
const Product = ({ product }) => {
return (
<SnackbarProvider maxSnack={3} preventDuplicate>
<ProductCard product={product} />
</SnackbarProvider>
);
};
export default Product;
const ProductCard = ({ product }) => {
const {
id,
imageUrls,
storeName,
category,
price,
amazonLink,
flipkartLink,
title,
} = product;
const iconColor = "var(--primaryColor)";
const { addProductWithQuantity } = useContext(CartContext); //Throws an error always
console.log(addProductWithQuantity);
const gotoURL = (location) => {
let a = document.createElement("a");
a.target = "_blank";
a.href = location;
a.click();
};
const handleClickVariant = (variant, title, id) => () => {
enqueueSnackbar(`Successfully added ${title} to cart`, {
variant,
});
onClickHeart(id);
// saveToLocal();
addProductWithQuantity(product);
};
const saveToLocal = () => {
let oldCart = localStorage.getItem("cart");
let newCart = oldCart ? JSON.parse(oldCart) : [];
newCart.push(product);
localStorage.setItem("cart", JSON.stringify(newCart));
};
const onClickHeart = (id) => {
document
.getElementById(id)
.style.setProperty("color", iconColor, "important");
};
const { enqueueSnackbar } = useSnackbar();
return (
<Card key={id} className={styles.card}>
<Link to={`/products/${id.trim()}`} className={styles.noDecoration}>
<div className={styles.thumbnail}>
<img src={imageUrls[0]} alt={title} />
</div>
</Link>
<div className={styles.name}>
<Typography className={styles.category} variant="subtitle2">
{category}
</Typography>
<Link to={`/products/${id.trim()}`} className={styles.noDecoration}>
<Typography className={styles.company} variant="subtitle1">
{title}
</Typography>
</Link>
</div>
<div className={styles.priceLike}>
<h4>₹{price}</h4>
<Tooltip title="Add to favourites" placement="top">
<IconButton
onClick={handleClickVariant("success", title, id)}
className={styles.likeIcon}
>
<i id={id} className={classNames("fas fa-cart-plus")} />
</IconButton>
</Tooltip>
</div>
<div className={styles.buttons}>
<Button
variant="contained"
color="primary"
className={styles.amazonBtn}
onClick={() => gotoURL(amazonLink)}
>
<img src={amazon} alt="amazon-link" /> amazon.in
</Button>
<Button
variant="contained"
color="primary"
className={styles.flipkartBtn}
onClick={() => gotoURL(flipkartLink)}
>
<img src={flipkart} alt="flipkart-link" />
</Button>
</div>
</Card>
);
};
}
The problem was I forgot to wrap my routes with CartContext Provider.
Here is what I did,
import CartProvider from '../CartContext';
return(
<CartProvider>
... Routes
<CartProvider>
)
I am new to React. Currently, I am doing API fetch and displaying it in a table and adding edit delete in that table. I am successfully fetching API and I can add a new row along with API data, but I don't know how to edit and delete it. I have seen some questions but my code structure is different so I am unable to find the answer. Does any help please?
here is my code,
import React from 'react';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import { makeStyles,withStyles } from '#material-ui/core/styles';
import Fab from '#material-ui/core/Fab';
import AddIcon from '#material-ui/icons/Add';
import EditIcon from '#material-ui/icons/Edit';
import TextField from '#material-ui/core/TextField';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import Button from '#material-ui/core/Button';
const useStyles = theme => ({
fab: {
margin: theme.spacing(1),
},
extendedIcon: {
marginRight: theme.spacing(1),
},
});
const Post = ({ body }) => {
return (
<table className=" table-striped">
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>Content</th>
</tr>
</thead>
<tbody>
{body.map(post => {
const { _id, title, content } = post;
return (
<tr key={_id}>
<td> {_id!=''?_id: '-'}</td>
<td> {title!='' ?title: '-'}</td>
<td> {content!=''?content: '-'}</td>
<hr />
</tr>
);
})}
</tbody>
</table>
);
};
class App extends React.Component {
state = {
isLoading: true,
posts: [],
error: null,
open:false,
newData:[
{
_id:'',
title:'',
content:''
}
]
};
fetchPosts() {
const axios = require('axios');
axios
.get("https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json")
.then(response => {
this.setState({
posts: response.data.posts,
isLoading: false,
})
//)
} )
}
componentDidMount() {
this.fetchPosts();
}
handleClickOpen = () =>
this.setState({
open:true
})
;
handleClose = () =>
this.setState({
open:false
})
;
// handleClick = () =>
// {
// {this.handleClickOpen}
// {this.addItem}
// }
// ;
addItem = () =>
{
var temp = [];
var tempPosts = this.state.posts;
var newData = this.state.newData[0];
console.log(this.state.newData)
if(this.state.newData[0]._id && this.state.newData[0].title && this.state.newData[0].content){
tempPosts.push(newData);
console.log(tempPosts)
this.setState({posts: tempPosts})
}
}
render() {
const { isLoading, posts } = this.state;
return (
<React.Fragment>
<h1>React Fetch - Blog</h1>
<div>
<Button variant="outlined" color="primary" onClick=
{this.handleClickOpen}
>
Add
</Button>
<Dialog open={this.state.open} onClose={this.handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Subscribe</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
id="_id"
label="id"
value={this.state.newData._id}
onChange={(e)=> {
let{ newData } = this.state;
newData[0]._id=e.target.value;
this.setState({newData})
}}
type="text"
fullWidth
/>
<TextField
autoFocus
margin="dense"
id="title"
label="title"
value={this.state.newData.title}
onChange={(e)=> {
let{newData} =this.state;
newData[0].title=e.target.value;
this.setState({newData})
}}
type="text"
fullWidth
/>
<TextField
autoFocus
margin="dense"
id="content"
label="content"
value={this.state.newData.content}
onChange={(e)=> {
let{newData} =this.state;
newData[0].content=e.target.value;
this.setState({newData})
}}
type="text"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
this.addItem()
this.handleClose()
}}
color="primary">
Add
</Button>
<Button onClick={this.handleClose} color="primary">
cancel
</Button>
</DialogActions>
</Dialog>
</div>
<hr />
{!isLoading ? <Post body={posts} /> : <h3>Loading...</h3>}
</React.Fragment>
);
}
}
export default withStyles(useStyles)(App);