I have been working with Material UI grid and I am trying to display data in a grid of cards that show in rows as opposed to a single column. My grid currently displays like this stacked in a column(please ignore the size of the cards this is just a zoomed in screenshot):
I would like the grid to display like this in rows as opposed to a single column:
I feel like I'm missing something simple but I'm just not sure why it keeps stacking in a single column. The code is as follows:
import React, { useState, useEffect } from "react"
// components
import Grid from "#material-ui/core/Grid"
import { makeStyles } from "#material-ui/core/styles"
import axios from "axios"
import RepoCard from "../components/RepoCard"
import Container from "#material-ui/core/Container"
// context
const Profile = () => {
const [data, setData] = useState(null)
const fetchGit = () => {
axios
.get(`https://api.github.com/users/rterrell25/repos?`)
.then((res) => {
setData(res.data)
console.log(res.data)
})
.catch((err) => console.log(err))
}
useEffect(() => {
fetchGit()
}, [])
return (
<div style={{ marginTop: 85 }}>
{!data ? (
<h1>Loading...</h1>
) : (
data.map((user) => (
<Container>
<Grid container spacing={4}>
<RepoCard name={user.name} />
</Grid>
</Container>
))
)}
</div>
)
}
export default Profile
the Repo Card Component code:
import React from "react"
import { Link } from "react-router-dom"
// MUI stuff
import Button from "#material-ui/core/Button"
import { makeStyles } from "#material-ui/core/styles"
import Card from "#material-ui/core/Card"
import CardActions from "#material-ui/core/CardActions"
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"
const useStyles = makeStyles((theme) => ({
icon: {
marginRight: theme.spacing(2),
},
heroContent: {
backgroundColor: theme.palette.background.paper,
padding: theme.spacing(8, 0, 6),
},
heroButtons: {
marginTop: theme.spacing(4),
},
cardGrid: {
paddingTop: theme.spacing(8),
paddingBottom: theme.spacing(8),
},
card: {
height: "100%",
display: "flex",
flexDirection: "column",
},
cardMedia: {
paddingTop: "56.25%", // 16:9
},
cardContent: {
flexGrow: 1,
},
footer: {
backgroundColor: theme.palette.background.paper,
padding: theme.spacing(6),
},
}))
const MovieCard = ({ name }) => {
const classes = useStyles()
return (
<Grid item xs={12} sm={6} md={4}>
<Card className={classes.card}>
<CardMedia
className={classes.cardMedia}
image={"#"}
title='Movie nane'
/>
<CardContent className={classes.cardContent}>
<Typography gutterBottom variant='h5' component='h2'>
"Movie name"
</Typography>
<Typography>Genre: Comedy</Typography>
<Typography noWrap>"Movie Summary"</Typography>
</CardContent>
<CardActions>
<Link to={""}>
<Button size='small' color='primary'>
Details
</Button>
</Link>
</CardActions>
</Card>
</Grid>
)
}
export default MovieCard
Change the xs={12} to xs={4}
The grid creates visual consistency between layouts while allowing flexibility across a wide variety of designs. Material Design’s responsive UI is based on a 12-column grid layout.
Refer: document of Material-UI Grid
Related
Excuse me for having to ask, but the following topic is costing me a lot.
I want to make a table with connections, but I can't find the formula to do it, and I couldn't find any example, I tried to find an example and copy its code but it doesn't convince me either. This is what I want to do:
enter image description here
The idea is that, this is how I currently have it:
`
import React from "react";
import { useState, useEffect } from "react";
import { makeStyles } from "#material-ui/core/styles";
import Card from "#material-ui/core/Card";
import CardActions from "#material-ui/core/CardActions";
import CardContent from "#material-ui/core/CardContent";
import Button from "#material-ui/core/Button";
import Typography from "#material-ui/core/Typography";
import Grid from "#material-ui/core/Grid";
import Paper from "#material-ui/core/Paper";
import MundialButtons from "../../componentes/mundial-buttons";
const useStyles = makeStyles((theme) => ({
root: {
minWidth: 100,
maxWidth: 250,
flexGrow: 1
},
bullet: {
display: "inline-block",
margin: "0 2px",
transform: "scale(0.8)"
},
title: {
fontSize: 14
},
pos: {
marginBottom: 12
},
paper: {
padding: theme.spacing(2),
textAlign: "center",
color: theme.palette.text.primary
},
logo: {
float: "left"
}
}));
export default function Two() {
const url =
"https://adad1EUmIOwosuGTI7L2DD6S02RjOG7vbxU3FjVVD1u-iYiw/a!A1:Z1000";
const [todos, setTodos] = useState();
const fetchApi = async () => {
const response = await fetch(url);
const responseJSON = await response.json();
setTodos(responseJSON);
};
useEffect(() => {
fetchApi();
}, []);
const classes = useStyles();
const styleRed2 = [
{ marginTop: "90px" },
{ marginTop: "180px" },
{ marginTop: "270px" }
];
return (
<div id="all">
<MundialButtons />
<div className={classes.root}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Paper className={classes.paper}>
<Typography>asd</Typography>
</Paper>
</Grid>
</Grid>
</div>
<div id="all">
{!todos
? "Cargando..."
: todos.map((todo, index) => {
return (
<Grid item xs={6}>
<div className={classes.root} id="octavos">
{todo.Local === undefined ||
todo.Local === "" ||
todo.Visitante === undefined ||
todo.Visitante === "" ? (
<div></div>
) : (
<Card className={classes.root} variant="outlined">
<CardContent>
<Typography
className={classes.title}
color="textSecondary"
gutterBottom
>
Octavos:
</Typography>
<Typography variant="h5" component="h2">
test
</Typography>
<Typography variant="h5" component="h2">
test
</Typography>
{/*<Typography
className={classes.pos}
color="textSecondary"
>
hora
</Typography>*/}
</CardContent>
</Card>
)}
<p></p>
</div>
</Grid>
);
})}
</div>
</div>
);
}
`
A box with connections between them
It's probably best to lean on open-source libraries for this, no need to reinvent the wheel. As it happens, what you require kind of already exists: https://www.npmjs.com/package/#g-loot/react-tournament-brackets. Demo.
I'm creating a Carousel similar to Netflix's, where if you hover over a card, it will enlarge it and show more details.
My problem is that, instead of enlarging just the hovered card, it's enlarging all the other cards.
Here's the component:
import * as React from 'react';
import {useState} from 'react';
import Card from '#mui/material/Card';
import CardHeader from '#mui/material/CardHeader';
import CardMedia from '#mui/material/CardMedia';
import CardContent from '#mui/material/CardContent';
import CardActions from '#mui/material/CardActions';
import Avatar from '#mui/material/Avatar';
import IconButton from '#mui/material/IconButton';
import Typography from '#mui/material/Typography';
import {red} from '#mui/material/colors';
import FavoriteIcon from '#mui/icons-material/Favorite';
import ShareIcon from '#mui/icons-material/Share';
import {makeStyles} from '#mui/styles';
const ctCardHoveredSx = {
maxWidth: 345,
"&:hover": {},
"& .ct-card": {
display: "none"
},
"&:hover .ct-card": {
display: "flex"
},
"& .ct-card-panel": {
transition: "transform 0.15s ease-in-out"
},
"&:hover .ct-card-panel": {
transform: "scale3d(1.05, 1.05, 1)"
}
}
const useStyles = makeStyles({
root: {
maxWidth: 310,
transition: "transform 0.15s ease-in-out",
"& .ct-card": {
display: "none"
}
},
cardHovered: {
transform: "scale3d(1.05, 1.05, 1)",
"&:hover .ct-card": {
display: "flex"
}
}
});
const CtCard = () => {
const classes = useStyles();
const [hovered, setHovered] = useState<boolean>(false);
return (
<Card className={classes.root}
classes={{root: hovered ? classes.cardHovered : ""}}
onMouseOver={() => setHovered(true)}
onMouseOut={() => setHovered(false)}
>
<CardMedia
component="img"
height="194"
image="https://picsum.photos/200/300"
alt="Paella dish"
/>
<CardHeader
avatar={
<Avatar sx={{bgcolor: red[500]}} aria-label="recipe">
R
</Avatar>
}
title="Lorem Ipsum"
subheader="Lorem Ipsum"
/>
<CardContent className={'ct-card'}>
<Typography variant="body2" color="text.secondary">
This impressive paella is a perfect party dish and a fun meal to cook
together with your guests. Add 1 cup of frozen peas along with the mussels,
if you like.
</Typography>
</CardContent>
<CardActions disableSpacing className={'ct-card'}>
<IconButton aria-label="add to favorites">
<FavoriteIcon/>
</IconButton>
<IconButton aria-label="share">
<ShareIcon/>
</IconButton>
</CardActions>
</Card>
);
}
export default CtCard;
And how it's used:
<Stack
position={'relative'}
direction={'row'}
spacing={1}
>
{
Array(pageCount).fill(null).map((v, i) =>
<CtCard/>
)
}
</Stack>
Perhaps start by try giving each <CtCard/> being mapped a unique key:
{
Array(pageCount)
.fill(null)
.map((v, i) => <CtCard key={i} />);
}
The index is not a good option for key either, but use just for testing purposes and switch to some actual unique ID later.
Based on your description the events setting state do fire as expected, so it would seem that it might be the updating of the components causing the issue.
I have implemented an MUI card grid to display contents of my infinite scroll. However, the last two elements of the returned contents are showing up larger for some reason. Also the grid wont display cards side by side without a map function, which I cannot use or my infinite scroll breaks.
All data is mock data
Any solutions to either part would be wonderful!
Infinite Scroll Component
`
import { useRef, useCallback, useState, useEffect } from 'react'
import {
Box,
CardMedia,
CardContent,
Grid,
Paper,
Typography,
} from "#mui/material";
import { styled } from '#mui/material/styles';
import CountryListCard from '../CountryListCard/CountryListCard';
import Card4 from '../CountryListCard/HomeCards/HomeCountryCard'
import { useInfiniteQuery } from 'react-query'
import { getPostsPage } from '../../services/countries.service'
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
gridContainer: {
paddingLeft: "40px",
paddingRight: "40px",
}
});
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
...theme.typography.body2,
padding: theme.spacing(2),
textAlign: 'center',
color: theme.palette.text.secondary,
}));
const HomeScroll = () => {
const classes = useStyles();
const {
fetchNextPage, //function
hasNextPage, // boolean
isFetchingNextPage, // boolean
data,
status,
error
} = useInfiniteQuery('/country', ({ pageParam = 1 }) => getPostsPage(pageParam), {
getNextPageParam: (lastPage, allPages) => {
return lastPage.length ? allPages.length + 1 : undefined
}
})
const intObserver = useRef()
const lastPostRef = useCallback(country => {
if (isFetchingNextPage) return
if (intObserver.current) intObserver.current.disconnect()
intObserver.current = new IntersectionObserver(countries => {
if (countries[0].isIntersecting && hasNextPage) {
console.log('We are near the last post!')
fetchNextPage()
}
})
if (country) intObserver.current.observe(country)
}, [isFetchingNextPage, fetchNextPage, hasNextPage])
if (status === 'error') return <p className='center'>Error: {error.message}</p>
const content = data?.pages.map(pg => {
return pg.map((country, i) => {
if (pg.length === i + 1) {
return (
<Grid container spacing={2}>
<Grid item xs>
<CountryListCard ref={lastPostRef} key={country.id} country={country} />
</Grid>
</Grid>
)
}
return (
<Grid container spacing={2}>
<Grid item xs={6}>
<CountryListCard key={country.id} country={country} />
</Grid>
</Grid>
)
})
})
return (
<>
<Grid container spacing={2}>
<Grid item xs={6}>
{content}
</Grid>
</Grid>
{/* <Review ref={lastPostRef} key={country._id} country={country} review={review}/> */}
{isFetchingNextPage && <p className="center">Loading More Posts...</p>}
<p className="center">Back to Top</p>
</>
)
}
export default HomeScroll;
Card Components
`
import {
CardMedia,
CardContent,
CardActions,
Grid,
Box,
Paper,
Typography,
} from "#mui/material";
import { makeStyles } from "#material-ui/core/styles";
import { styled } from '#mui/material/styles';
import React, { useState, useEffect, useContext } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import MenuList from "../menu/menuItem";
import Flash from "../../components/Flash";
import { AuthContext } from "../../services/auth.context";
import Card4 from './HomeCards/HomeCountryCard';
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
...theme.typography.body2,
padding: theme.spacing(2),
textAlign: 'center',
color: theme.palette.text.secondary,
}));
const useStyles = makeStyles({
gridContainer: {
paddingLeft: "40px",
paddingRight: "40px"
}
});
const CountryListCard = React.forwardRef(({ country }, ref) => {
const location = useLocation();
const navigate = useNavigate();
const { isAuthenticated, user } = useContext(AuthContext);
const classes = useStyles();
const rows =[{
country: country,
}]
const postBody = (
<>
<Card4 country={country} key={country.id}/>
</>
)
const content = ref
? <div ref={ref}>{postBody}</div>
: <div>{postBody}</div>
const handleViewClick = () => {
navigate(`/country/${country._id}`);
};
return (
<>
{location.state?.message && (
<Flash type={location.state.type} message={location.state.message} />
)}
{content}
{user && isAuthenticated && (
<Grid item xl={6} md={6} sm={12} xs={12}>
<MenuList countryId={country._id} />
</Grid>
)}
</>
);
})
export default CountryListCard;
`
`
import {
Rating,
Grid,
} from "#mui/material";
import React, { useState, useEffect, useContext } from "react";
import { useNavigate } from "react-router-dom";
import { Card, Col, Row, Button, Text } from "#nextui-org/react";
const Card4 = ({ country }) => {
const navigate = useNavigate();
const handleViewClick = () => {
navigate(`/country/${country._id}`);
};
return (
<>
<Card isPressable>
<Card.Body css={{ p: 0 }}>
<Card.Image
src={country.images[0].image}
objectFit="cover"
width="100%"
height={140}
alt={country.title}
/>
<Rating name="simple-controlled" value={country.overallRating} readOnly/>
</Card.Body>
<Card.Footer css={{ justifyItems: "flex-start" }}>
<Row wrap="wrap" justify="space-between" align="center">
<Text b>{country.title}</Text>
<Button flat auto rounded color="secondary"
onClick={handleViewClick}
>
<Text
css={{ color: "inherit" }}
size={12}
weight="bold"
transform="uppercase"
>
View Country
</Text>
</Button>
</Row>
</Card.Footer>
</Card>
</>
);
};
export default Card4
Picture of the problem
picture](https://i.stack.imgur.com/QObLp.png)
I tried adding the mui code to all different elements, I tried using a map function instead of infinite scroll but that was unfeasible, I tried other frontend libraries and got the same issues. I also tried changing all kinds of different spacing and sizing of elements.
I'm experiencing an issue correctly rendering data in a basic component?
questionThree.js
import React, {Component} from 'react';
import { makeStyles } from '#material-ui/core/styles';
import List from '#material-ui/core/List';
import Paper from '#material-ui/core/Paper';
import Android from "#material-ui/icons/Android";
import Pets from "#material-ui/icons/Pets";
import BugReport from "#material-ui/icons/BugReport";
import QuestionListItem from './questionListItem';
import { createRowData } from './mocks';
const createMockData = () =>{
/* Please do not refactor this function */
return [
createRowData({species: 'Robot', name: 'T-100', Icon: Android, description: "Likes long walks, walking over the bones of it's enemies"}),
createRowData({species: 'Bug', name:'Barry', Icon: BugReport, description: "Likes long walks, and creating problems in all your code"}),
createRowData({species: 'Rabbit', name:'Roger', Icon: Pets, description: "Likes long walks and getting to know the inner you"}),
createRowData({species: null, name: null, Icon: null, description: null}),
]
};
const useStyles = makeStyles(() => ({
container:{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100%',
},
root: {
width: '100%',
},
inline: {
display: 'inline',
},
}));
class questionThree extends Component {
render(){
const classes = useStyles();
const mockData = createMockData();
return (
<div className={classes.container}>
<Paper>
<List className={classes.root}>
{mockData.map((item, i) =>{
return <QuestionListItem item={item} key={item.id} divider={i !== mockData.length -1}/>
})}
</List>
</Paper>
</div>
);
}
}
export default questionThree
*question.js*
import React from 'react'
import Typography from "#material-ui/core/Typography";
import Solution from './images/solution.png'
import { CardMedia } from '#material-ui/core';
const question = ()=>{
return (
<div>
<Typography variant="h4" gutterBottom>
Question Three
</Typography>
<Typography variant="h5" gutterBottom>
List on the fritz
</Typography>
<Typography variant="body1" gutterBottom>
This task revolves around a bug in the render method of a basic component.
</Typography>
<Typography variant="body1" gutterBottom>
Your task if you choose to accept it, is to resolve this bug (displayed on the right and in the console) .
</Typography>
<Typography variant="body1" gutterBottom>
As with all the questions in this tech test, you may or may not wish to refactor some of the code.
</Typography>
<Typography variant="h6" gutterBottom>
Below is what the final solution should look like. (GUID'S will vary)
</Typography>
<CardMedia
image={Solution}
style={{
width: '100%',
height: 500,
backgroundSize: 'contain',
}}
title="The Solution"
/>
</div>
)
};
export default question
index.js
import React from 'react';
import QuestionThree from './questionThree';
import Question from './question'
import ErrorBoundary from '../../components/errorBoundary'
const questionThree = () =>{
return (
<ErrorBoundary question={Question}>
<QuestionThree/>
</ErrorBoundary>
)
}
export default questionThree;
I've tried a number of solutions and most of the solutions available on Google and nothing seems to work. I did try the solution provided at https://reactjs.org/warnings/invalid-hook-call-warning.html, however I hit an issue with getting the results back from the second step of the instruction.
What am I missing? Is this a 'I should've gone to Specsavers' moment?
makeStyles returns a hook called useStyles which can only be called inside a functional component. That is why you are getting the error.
In your case change questionThree to be a functional component
const questionThree = (props) => {
const classes = useStyles();
const mockData = createMockData();
return (
<div className={classes.container}>
<Paper>
<List className={classes.root}>
{mockData.map((item, i) =>{
return <QuestionListItem item={item} key={item.id} divider={i !== mockData.length -1}/>
})}
</List>
</Paper>
</div>
);
}
export default questionThree
To use it for class components, make use of withStyles HOC
import { withStyles } from '#material-ui/core/styles';
const styles = {
container:{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100%',
},
root: {
width: '100%',
},
inline: {
display: 'inline',
},
}
class questionThree extends Component {
render(){
const {classes} = props
const mockData = createMockData();
return (
<div className={classes.container}>
<Paper>
<List className={classes.root}>
{mockData.map((item, i) =>{
return <QuestionListItem item={item} key={item.id} divider={i !== mockData.length -1}/>
})}
</List>
</Paper>
</div>
);
}
}
export default withStyles(styles)(questionThree);
I'm having really bizarre issue with my code. I am using React to map a Redux state onto Material-UI cards. When I was testing different styling on my cards the other day I was able to make different classes and update the card appearance however I liked. However today when I added new information to my database the new cards will not take all of the styling from the Material-UI database. Specifically it seems like the new cards are not effected by justifyContent.
This is a difficult issue for me to explain but basically I have a database of information being mapped onto multiple different cards. Some of the cards are accepting the Material-UI styling other cards are not.
I would like to post a jsfiddle but I would need to link it to my database and I'm not sure that can be done.
Here is the page which is mapping my redux state:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import ProjectItem from '../ProjectItem/ProjectItem'
import {withRouter} from 'react-router-dom'
import Header from '../Header/Header'
import { withStyles } from '#material-ui/core';
const mapReduxStateToProps = (reduxState) => ({reduxState});
const styles = theme => ({
bodyContent: {
marginTop: 100,
}
})
class ProjectPage extends Component {
//This calls the getProjects function on the page load
componentDidMount() {
this.getProjects();
}
//This function dispatches an action to our index page
getProjects = () => {
console.log('running')
this.props.dispatch({ type: 'GET_PROJECT'})
}
render() {
const {classes} = this.props
return (
<div>
<Header />
<div className= {classes.bodyContent}>
{this.props.reduxState.projects.map((project,index) => {
return(
<ProjectItem
key = {index}
id = {project.id}
name ={project.name}
description = {project.description}
thumbnail = {project.thumbnail}
website = {project.website}
github = {project.github}
tag_id = {project.tag_id}
/>
)
})}
</div>
</div>
);
}
}
export default withRouter(connect(mapReduxStateToProps)(withStyles(styles)(ProjectPage)));
And here is the page with my styling.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import './ProjectItem.css'
import Card from '#material-ui/core/Card';
import CardMedia from '#material-ui/core/CardMedia'
import CardContent from '#material-ui/core/CardContent'
import Typography from '#material-ui/core/Typography'
import { withStyles } from '#material-ui/core';
const mapReduxStateToProps = (reduxState) => ({reduxState});
const styles = theme => ({
card: {
display: 'flex',
width: 550,
alignSelf: 'center',
height: 150,
paddingTop: 5,
paddingBottom:5,
},
details: {
display: 'flex',
flexDirection: 'column'
},
thumbnail: {
height: 150,
minWidth: 150
},
contentBody: {
justifyContent: 'space-between',
textAlign: 'left',
alignItems: 'center',
display : 'inline-flex',
},
toolbar: theme.mixins.toolbar
})
class ProjectItem extends Component {
render() {
const {classes} = this.props
return (
<div className='container'>
<Card className={classes.card}>
<CardMedia
className={classes.thumbnail}
image={this.props.thumbnail}
title="Project Image"
/>
<div className={classes.details}>
<CardContent className={classes.contentBody}>
<Typography variant='h5'>
{this.props.name}
</Typography>
<Typography variant='h6'>
<a href={this.props.github}>GitHub</a>
</Typography>
<Typography variant='h6'>
<a href={this.props.website}>Website</a>
</Typography>
<Typography variant='h6'>
{this.props.tag_id}
</Typography>
</CardContent>
<CardContent className={classes.content}>
<Typography>
{this.props.description}
</Typography>
</CardContent>
</div>
</Card>
</div>
);
}
}
export default connect(mapReduxStateToProps)(withStyles(styles)(ProjectItem));