How to set the style a react.js component when creating it?
Below is some of my code (partially inherited from a stronger developer and then simplified for brevity).
I want to re-use my LogComponent to print several pages of a Log. However, in some cases I want to force a particular width on the returned List, rather than allowing it to flex as it sees fit.
I would prefer to not define a separate LogComponentFixed or to have an if (...) {return (...)} else {return(...)} in my LogComponent.
I have in mind to do something in Log.js like:
<LogComponent heading={"Page 1"}, lines={page_1}, style={styles.list_1} />
<LogComponent heading={"Page 1"}, lines={page_1}, style={styles.list_2} />
And to then, in LogComponent do something like:
<List style={style}> ... </List>
I also tried using something like
<List className={list_1}> ... </List>
But none of the things I've tried works...
Log.js
import React from 'react'
import Typography from '#material-ui/core/Typography'
import { withStyles } from '#material-ui/core/styles'
import LogComponent from './LogComponent'
const styles = theme => ({
title: {
padding: theme.spacing.unit*1.5,
},
list_1: {
},
list_2: {
width: "300px"
},
listContainer: {
flexGrow: 1,
minHeight: 0,
overflow: 'auto'
},
})
const Log = ({classes, log}) => {
const page_1 = log[0];
const page_2 = log[1];
return (
<div>
<Typography className={classes.title} color="textSecondary" key={1}>
Example Log
</Typography>
<div className={classes.listContainer} key={2}>
<LogComponent heading={'Page 1'} lines={page_1} />
<LogComponent heading={'Page 2'} lines={page_2} />
</div>
</div>
export default withStyles(styles)(Log)
LogComponent.js
import React from 'react'
import Typography from '#material-ui/core/Typography'
import { withStyles } from '#material-ui/core/styles'
import { List, ListItem, ListItemText } from '#material-ui/core';
const styles = theme => ({
title: {
padding: theme.spacing.unit*1.5,
},
}
const LogComponent = ({classes, list_class, heading, lines}) => {
return (
<div className={classes.root}>
<Typography className={classes.title} color="textSecondary" key={1}>
{heading}
</Typography>
<div>
<List dense>
{[...lines[0]].map(e =>
<ListItem><ListItemText primary={e} /></ListItem>
)}
</List>
</div>
</div>
)
}
export default withStyles(styles)(LogComponent)
Here you are sending the styles as a prop to LogComponent, that's why it will not be applied as styles to that component you have created. The style attribute is for HTML tags and in material-ui, you can pass styles to a wrapper component also.
In your case you can get the styles inside your LogComponent as below:
Send styles as a prop as you mentioned in the question
<LogComponent heading={"Page 1"}, lines={page_1}, style={styles.list_1} />
Now, you can access it from props,
// right below get the style
const LogComponent = ({classes, list_class, heading, lines, style}) => {
return (
<div className={classes.root} style={style}> // Look style attribute added, style(value) is from props
<Typography className={classes.title} color="textSecondary" key={1}>
{heading}
</Typography>
<div>
<List dense>
{[...lines[0]].map(e =>
<ListItem><ListItemText primary={e} /></ListItem>
)}
</List>
</div>
</div>
)
}
Related
I'm trying to use breakpoints for responsive design in my page, but it dosent really seem to work. Whenever i apply any breakpoint, the whole page goes blank.
Here's my code:-
Styles.js
import { makeStyles } from "#mui/styles";
const useStyles = makeStyles((theme) => ({
title: {
display: 'none',
[theme.breakpoints.up('sm')]: {
display: 'block',
},
},
}));
export default useStyles;
Header.jsx
import React from 'react';
import { Autocomplete } from '#react-google-maps/api';
import { AppBar, Toolbar, Typography, InputBase, Box } from '#mui/material';
import SearchIcon from '#mui/icons-material/Search';
import useStyles from './styles';
const Header = () => {
const classes = useStyles();
return (
<>
<AppBar position='static'>
<Toolbar className={classes.toolbar}>
<Typography variant='h5' className={classes.title}>
Travel Advisor
</Typography>
{/* Box acts as Div */}
<Box display='flex'>
<Typography variant='h6' className={classes.title}>
Explore new places
</Typography>
{/* <Autocomplete> */}
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase placeholder='Search...' classes={{ root: classes.inputRoot, input: classes.inputInput }} />
</div>
{/* </Autocomplete> */}
</Box>
</Toolbar>
</AppBar>
</>
)
}
export default Header;
Before applying breakpoints :-
Before breakpoint
After applying breakpoints :-
After breakpoint
Make sure that you created breakpoints correctly.
https://mui.com/material-ui/customization/breakpoints/
And check if you have wrapped your app in ThemeProvider.
And I want to note that #mui/styles is deprecated. Perhaps this problem may be related to this.
https://mui.com/system/styles/basics/
⚠️ #mui/styles is the legacy styling solution for MUI. It depends on JSS as a styling solution, which is not used in the #mui/material anymore, deprecated in v5. If you don't want to have both Emotion & JSS in your bundle, please refer to the #mui/system documentation which is the recommended alternative.
⚠️ #mui/styles is not compatible with React.StrictMode or React 18.
Dont use it on newly created app.
I'm in the process of building out a simple react act that display REST data from my localhost URL.
I keep getting this error and I'm not sure why, at first I thought it was the data within the API itself but I think that's not the case for this?
I am not getting any npm start errors, the error appears when I inspect a page with browser tools.
Here is the full error:
index.js:1 Warning: Encountered two children with the same key, `1`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
at div
at Grid (http://localhost:4000/static/js/0.chunk.js:1556:35)
at WithStyles(ForwardRef(Grid)) (http://localhost:4000/static/js/0.chunk.js:6385:31)
at main
at Container (http://localhost:4000/static/js/0.chunk.js:1101:23)
at WithStyles(ForwardRef(Container)) (http://localhost:4000/static/js/0.chunk.js:6385:31)
at UserBuckets (http://localhost:4000/static/js/main.chunk.js:363:5)
at LoadingComponent (http://localhost:4000/static/js/main.chunk.js:999:5)
at div
at App (http://localhost:4000/static/js/main.chunk.js:173:89)
at Route (http://localhost:4000/static/js/0.chunk.js:48473:29)
at Switch (http://localhost:4000/static/js/0.chunk.js:48675:29)
at Router (http://localhost:4000/static/js/0.chunk.js:48108:30)
at BrowserRouter (http://localhost:4000/static/js/0.chunk.js:47728:35)
Could someone point out what is causing this error in my code? I haven't been able to solve it myself.
Here is my required code:
App.js
import React, { useEffect, useState } from 'react';
import './App.css';
import UserBuckets from './components/BucketLists';
import LoadingComponent from './components/Loading';
function App() {
const ListLoading = LoadingComponent(UserBuckets);
const [appState, setAppState] = useState({
loading: false,
buckets: null,
});
useEffect(() => {
setAppState({ loading: true });
const apiUrl = `http://127.0.0.1:8000/api/`;
fetch(apiUrl)
.then((data) => data.json())
.then((buckets) => {
setAppState({ loading: false, buckets: buckets });
});
}, [setAppState]);
return (
<div className="App">
<h1>Latest Buckets</h1>
<ListLoading isLoading={appState.loading} buckets={appState.buckets} />
</div>
);
}
export default App;
bucketList.js
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Card from '#material-ui/core/Card';
import CardContent from '#material-ui/core/CardContent';
import CardMedia from '#material-ui/core/CardMedia';
import Grid from '#material-ui/core/Grid';
import Typography from '#material-ui/core/Typography';
import Container from '#material-ui/core/Container';
const useStyles = makeStyles((theme) => ({
cardMedia: {
paddingTop: '56.25%', // 16:9
},
link: {
margin: theme.spacing(1, 1.5),
},
cardHeader: {
backgroundColor:
theme.palette.type === 'light'
? theme.palette.grey[200]
: theme.palette.grey[700],
},
bucketTitle: {
fontSize: '16px',
textAlign: 'left',
},
bucketText: {
display: 'flex',
justifyContent: 'left',
alignItems: 'baseline',
fontSize: '12px',
textAlign: 'left',
marginBottom: theme.spacing(2),
},
}));
const UserBuckets = (props) => {
const { buckets } = props;
const classes = useStyles();
if (!buckets || buckets.length === 0) return <p>Can not find any buckets, sorry</p>;
return (
<React.Fragment>
<Container maxWidth="md" component="main">
<Grid container spacing={5} alignItems="flex-end">
{buckets.map((buckets) => {
return (
// Enterprise card is full width at sm breakpoint
<Grid item key={buckets.owner} xs={12} md={4}>
<Card className={classes.card}>
<CardMedia
className={classes.cardMedia}
image="https://source.unsplash.com/random"
title="Image title"
/>
<CardContent className={classes.cardContent}>
<Typography
gutterBottom
variant="h6"
component="h2"
className={classes.bucketTitle}
>
{buckets.name.substr(0, 50)}...
</Typography>
<div className={classes.bucketText}>
<Typography
component="p"
color="textPrimary"
></Typography>
<Typography variant="p" color="textSecondary">
{buckets.stock_list}...
</Typography>
</div>
</CardContent>
</Card>
</Grid>
);
})}
</Grid>
</Container>
</React.Fragment>
);
};
export default UserBuckets;
Loading.js
import React from 'react';
function LoadingComponent(Component) {
return function LoadingComponent({ isLoading, ...props }) {
if (!isLoading) return <Component {...props} />;
return (
<p style={{ fontSize: '25px' }}>
We are waiting for the data to load!...
</p>
);
};
}
export default LoadingComponent;
Thank in advance...
The error came from this culprit and my mistake not seeing the important of the letter key in item key. This is how I solved my error:
original code
<Grid item key={buckets.owner} xs={12} md={4}>
fixed code
<Grid item key={buckets.id} xs={12} md={4}>
I get the above error when I try to display {props.child}.The page remains blank.The detailed warning is
index.js:1 Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
in div (at CustomLayout.js:28)
in main (created by Basic)
in Basic (created by Context.Consumer)
in Content (at CustomLayout.js:22)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Layout (at CustomLayout.js:8)
in CustomLayout (at App.js:10)
in div (at App.js:9)
in App (at src/index.js:7)
Below are the project files. Article.js is a component and ArticleListView and CustomLayout are containers to it. I am trying to access the child elements in CustomLayout.js by {props.children}
App.js
import React from 'react';
import './App.css';
import 'antd/dist/antd.css';
import CustomLayout from './containers/CustomLayout'
import ArticleListView from './containers/ArticleListView';
function App() {
return (
<div className="App">
<CustomLayout>
{ArticleListView}
</CustomLayout>
</div>
);
}
export default App
ArticleListView.js
import React from 'react'
import Article from '../components/Article'
class ArticleListView extends React.Component{
render(){
return(
<Article/>
);
}
}
export default ArticleListView
Article.js
import React from 'react'
import { List, Avatar, Icon } from 'antd';
const listData = [];
for (let i = 0; i < 23; i++) {
listData.push({
href: 'http://ant.design',
title: `ant design part ${i}`,
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
description:
'Ant Design, a design language for background applications, is refined by Ant UED Team.',
content:
'We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.',
});
}
const IconText = ({ type, text }) => (
<span>
<Icon type={type} style={{ marginRight: 8 }} />
{text}
</span>
);
function Article(props){
return(
<List
itemLayout="vertical"
size="large"
pagination={{
onChange: page => {
console.log(page);
},
pageSize: 3,
}}
dataSource={listData}
footer={
<div>
<b>ant design</b> footer part
</div>
}
renderItem={item => (
<List.Item
key={item.title}
actions={[
<IconText type="star-o" text="156" key="list-vertical-star-o" />,
<IconText type="like-o" text="156" key="list-vertical-like-o" />,
<IconText type="message" text="2" key="list-vertical-message" />,
]}
extra={
<img
width={272}
alt="logo"
src="https://gw.alipayobjects.com/zos/rmsportal/mqaQswcyDLcXyDKnZfES.png"
/>
}
>
<List.Item.Meta
avatar={<Avatar src={item.avatar} />}
title={<a href={item.href}>{item.title}</a>}
description={item.description}
/>
{item.content}
</List.Item>
)}
/>
);
}
export default <Article/>
CustomLayout.js
import React from 'react'
import { Layout, Menu, Breadcrumb } from 'antd';
const { Header, Content, Footer } = Layout;
function CustomLayout(props){
return(
<Layout className="layout">
<Header>
<div className="logo" />
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={['2']}
style={{ lineHeight: '64px' }}
>
<Menu.Item key="1">nav 1</Menu.Item>
<Menu.Item key="2">nav 2</Menu.Item>
<Menu.Item key="3">nav 3</Menu.Item>
</Menu>
</Header>
<Content style={{ padding: '0 50px' }}>
<Breadcrumb style={{ margin: '16px 0' }}>
<Breadcrumb.Item>Home</Breadcrumb.Item>
<Breadcrumb.Item>List</Breadcrumb.Item>
<Breadcrumb.Item>App</Breadcrumb.Item>
</Breadcrumb>
<div style={{ background: '#fff', padding: 24, minHeight: 280 }}>{props.children}</div>
</Content>
<Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer>
</Layout>
);
}
export default CustomLayout
If you are using any javascript expression then only use curly braces like - {a+b}.
but form html tags or react component you need to import as per react standard.
Use like this
<CustomLayout>
<ArticleListView />
</CustomLayout>
and change your export default <Article/> to export default Article
It should be <ArticleListView/>, not {ArticleListView}
Try
<CustomLayout>
<ArticleListView />
</CustomLayout>
rather than
<CustomLayout>
{ArticleListView}
</CustomLayout>
in javascript we work like isConditionTrue ? screenOne :screenTwo
but in typescript we have to change isConditionTrue ? :
I have built a Trello clone using ReactJS, where I have 4 columns called TODO, DOING, DONE and REJECTED, where I can add a card to any column.
In a file I am trying to map over card component and rendering properties from defined dummy data.
What I want to do?
I want to delete a specific card when the button is clicked.
What I tried?
I have added the functionality in my Redux store, but when adding the onclick event to my button, I cannot access the dispatch method which will trigger the deleteCard function.
How do I do that?
My TaskboardList.js component :
import React from "react";
import TaskboardCard from "./TaskboardCard";
import TaskboardActionButton from "./TaskboardActionButton";
import { Droppable } from "react-beautiful-dnd";
const TaskboardList = ({ title, cards, listID }) => {
return (
<Droppable droppableId={String(listID)}>
{provided => (
<div
className="taskboardlist_container"
{...provided.droppableProps}
ref={provided.innerRef}
style={styles.container}
>
<div className="sub-heading">{title}</div>
{cards.map((card, index) => (
<TaskboardCard
key={card.id}
index={index}
text={card.text}
id={card.id}
/>
))}
<TaskboardActionButton listID={listID} />
{provided.placeholder}
</div>
)}
</Droppable>
);
};
const styles = {
container: {
backgroundColor: "#eee",
width: 300,
padding: "0.5rem",
marginRight: "1rem",
height: "100%"
}
};
export default TaskboardList;
My TaskboardCard.js component
import React from "react";
import Card from "#material-ui/core/Card";
import Typography from "#material-ui/core/Typography";
import CardContent from "#material-ui/core/CardContent";
import { Draggable } from "react-beautiful-dnd";
import { connect } from "react-redux";
import { deleteCard } from "../actions";
const TaskboardCard = ({ text, id, index, sample, cardId }) => {
// handleClickDelete = () => {
// // const { dispatch } = this.props;
// // dispatch(deleteCard(cardId));
// console.log("clicked");
// };
return (
<Draggable draggableId={String(id)} index={index}>
{provided => (
<div
className="taskboard_container"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Card>
<CardContent>
<Typography style={{ fontSize: "1.5rem" }} gutterBottom>
{text}
</Typography>
</CardContent>
</Card>
{/* //delete added */}
<button
onClick={(cardId, props, sample, dispatch) => {
//const { dispatch } = this.props;
dispatch(deleteCard(cardId));
}}
>
DELETE
</button>
{/* ////////////////////// */}
</div>
)}
</Draggable>
);
};
export default connect()(TaskboardCard);
In the above component delete button is not working because somehow i cannot access the dispatch.
Here is my codesandbox link for further reference to files https://codesandbox.io/s/github/abhinav-anshul/consensolabs
Remove the props and dispatch from onclick event and add dispatch in component parameter list.
If you don't specify the second argument to connect(), your component will receive dispatch by default in porps.
import React from "react";
import Card from "#material-ui/core/Card";
import Typography from "#material-ui/core/Typography";
import CardContent from "#material-ui/core/CardContent";
import { Draggable } from "react-beautiful-dnd";
import { connect } from "react-redux";
import { deleteCard } from "../actions";
const TaskboardCard = ({ text, id, index, sample, cardId, dispatch }) => {
// handleClickDelete = () => {
// // const { dispatch } = this.props;
// // dispatch(deleteCard(cardId));
// console.log("clicked");
// };
return (
<Draggable draggableId={String(id)} index={index}>
{provided => (
<div
className="taskboard_container"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Card>
<CardContent>
<Typography style={{ fontSize: "1.5rem" }} gutterBottom>
{text}
</Typography>
</CardContent>
</Card>
{/* //delete added */}
<button
onClick={(cardId, sample) => {
//const { dispatch } = this.props;
dispatch(deleteCard(cardId));
}}
>
DELETE
</button>
{/* ////////////////////// */}
</div>
)}
</Draggable>
);
};
export default connect()(TaskboardCard);
I have a website built with Gatsby.js using the Material-UI.
Specific problem is this: I want to use the Google Tag Manager "Element Visibility" triggers. If some HTML element becomes visible, GTM should fire some GA tag.
Question is this: how can I specify the HTML ID for a material-ui component for GTM (or anything else) to find it?
First example:
// ...react imports omitted...
import makeStyles from '#material-ui/core/styles/makeStyles';
import Box from '#material-ui/core/Box';
import Grid from '#material-ui/core/Grid';
import CloseIcon from '#material-ui/icons/Close';
import Link from '~components/Link';
import ButtonSubmit from '~components/form-buttons/ButtonSubmit';
import Container from '~components/Container';
// ... all other imports are in-house code
const useStyles = makeStyles(theme => ({ /* ...styles... */}));
const GuestUserSoftSaleSecondPopup = ({ which, ...rest }) => {
const classes = useStyles();
// ...setup code omitted...
return (
<Box bgcolor="#474d5c" width="100%" py={4} className={classes.banner}>
<Container>
<Grid container direction="row" justify="space-between" alignItems="center" spacing={2}>
<Grid item xs={12} sm={1} md={3} lg={4}>
<CloseIcon onClick={handleClose} size="large" className={classes.closeIcon} />
</Grid>
<Grid item xs={12} sm={7} md={5} lg={4}>
<Link to="/subscribe" variant="h5" className={classes.linkStyle}>
Become a member for full access
</Link>
</Grid>
<Grid item xs={12} sm={4} className={classes.buttonPosition}>
<Link to="/subscribe" underline="none" className={classes.linkStyle}>
<ButtonSubmit type="button" fullWidth={false}>
See my option
</ButtonSubmit>
</Link>
</Grid>
</Grid>
</Container>
</Box>
);
};
// ...proptypes and `export` clause
Second example:
// ...react imports omitted...
import makeStyles from '#material-ui/core/styles/makeStyles';
import MuiDialog from '#material-ui/core/Dialog';
const useStyles = makeStyles(() => ({ /* ...styles... */ }));
const Dialog = ({ children, background, backdrop, isOpen, ...rest }) => {
const classes = useStyles({ background });
return (
<MuiDialog
open={isOpen}
maxWidth="sm"
fullWidth
disableBackdropClick
disableEscapeKeyDown
BackdropProps={{
className: backdrop ? classes.backdropBM : classes.backdrop
}}
PaperProps={{
className: classes.paper
}}
scroll="body"
{...rest}
>
{children}
</MuiDialog>
);
};
If you look at the API documentation for almost any of the Material-UI components, you will find at the end of the "Props" section a statement like the following example from Dialog:
Any other props supplied will be provided to the root element (Modal).
This means that any props not explicitly recognized by this component will be passed along eventually to whatever HTML element is the outermost element rendered. So for most Material-UI components, in order to add an id property, you just specify it.
My example below (a modification of the Simple Dialog demo) includes three different ids: one on the Dialog element which will be placed on the outermost div of the Modal, one specified via the PaperProps which will go on the main Paper div of the visible content of the dialog, and one on the Box wrapped around the dialog content.
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import Avatar from "#material-ui/core/Avatar";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemAvatar from "#material-ui/core/ListItemAvatar";
import ListItemText from "#material-ui/core/ListItemText";
import DialogTitle from "#material-ui/core/DialogTitle";
import Dialog from "#material-ui/core/Dialog";
import PersonIcon from "#material-ui/icons/Person";
import Typography from "#material-ui/core/Typography";
import { blue } from "#material-ui/core/colors";
import Box from "#material-ui/core/Box";
const emails = ["username#gmail.com", "user02#gmail.com"];
const useStyles = makeStyles({
avatar: {
backgroundColor: blue[100],
color: blue[600]
}
});
function SimpleDialog(props) {
const classes = useStyles();
const { onClose, selectedValue, open } = props;
const handleClose = () => {
onClose(selectedValue);
};
const handleListItemClick = value => {
onClose(value);
};
return (
<Dialog
onClose={handleClose}
aria-labelledby="simple-dialog-title"
open={open}
PaperProps={{ id: "MyDialogPaperID" }}
id="ThisIDWillBeOnTheModal"
>
<DialogTitle id="simple-dialog-title">Set backup account</DialogTitle>
<Box id="MyBoxID">
<List>
{emails.map(email => (
<ListItem
button
onClick={() => handleListItemClick(email)}
key={email}
>
<ListItemAvatar>
<Avatar className={classes.avatar}>
<PersonIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={email} />
</ListItem>
))}
</List>
</Box>
</Dialog>
);
}
SimpleDialog.propTypes = {
onClose: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
selectedValue: PropTypes.string.isRequired
};
export default function SimpleDialogDemo() {
const [open, setOpen] = React.useState(false);
const [selectedValue, setSelectedValue] = React.useState(emails[1]);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = value => {
setOpen(false);
setSelectedValue(value);
};
return (
<div>
<Typography variant="subtitle1">Selected: {selectedValue}</Typography>
<br />
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open simple dialog
</Button>
<SimpleDialog
selectedValue={selectedValue}
open={open}
onClose={handleClose}
/>
</div>
);
}
Material UI components don't let you set an id for them since the implementation inside should be a black box and may contain multiple html element. See if you can wrap the element in a div and put the id on that instead.
Another option would be to add a class (via the classes prop) to the element instead but I'm not sure if Google Tag Manager can use those instead of ids.