i recently start to learn react. I made card component using Material UI for react, but this time i'm going to make it with axios and map().
What i expected was, the cards should be in the same row, not vertical.
This is how the cards look when using axios and map()
This the frontend code using React, axios, and map()
import { makeStyles, withStyles } from "#material-ui/styles";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import axios from "axios";
//Material UI
import { Grid } from "#material-ui/core";
import Box from "#mui/material/Box";
import Card from "#mui/material/Card";
import CardActions from "#mui/material/CardActions";
import CardContent from "#mui/material/CardContent";
import Button from "#mui/material/Button";
import Typography from "#mui/material/Typography";
const useStyles = makeStyles({
gridContainer: {
paddingLeft: "40px",
paddingRight: "40px",
},
root: {
minWidth: 200,
},
bullet: {
display: "inline-block",
margin: "0 2px",
transform: "scale(0.8)",
},
title: {
fontSize: 14,
},
pos: {
marginBottom: 12,
},
});
function Home() {
const classes = useStyles();
const [getData, setGetData] = useState([]);
useEffect(() => {
axios.get("http://127.0.0.1:8080/api/get.php").then((x) => {
setGetData(x.data);
});
}, [getData]);
return (
<div>
{getData.map((x) => {
return (
<Grid
container
spacing={4}
className={classes.gridContainer}
justify="center"
style={{ marginTop: "80px" }}
>
<Grid item xs={12} sm={6} md={4}>
<Card className={classes.root} variant="outlined">
<CardContent>
<Typography
className={classes.title}
color="textSecondary"
gutterBottom
>
Word of the Day
</Typography>
<Typography className={classes.pos} color="textSecondary">
adjective
</Typography>
<Typography variant="body2" component="p">
well meaning and kindly.
<br />
{'"a benevolent smile"'}
</Typography>
</CardContent>
<CardActions>
<Button size="small">Learn More</Button>
</CardActions>
</Card>
</Grid>
</Grid>
);
})}
</div>
);
}
export default Home;
Expected output that i looking for before using map() method
Did i miss some configuration? or styling? grid? container?
<Grid container> should be outside the loop. I would also consider using Material UI v5. Don't forget about key prop for looped items.
<Grid
container
spacing={4}
className={classes.gridContainer}
justifyItems="center"
style={{ marginTop: "80px" }}
>
{getData.map((x, index) => (
<Grid key={index} item xs={12} sm={6} md={4}>
...
</Grid>
)}
</Grid>
Move your Grid Container outside of map function. You have to wrap 3 cards inside the Grid Container
The problem you're having is that you are wrapping each component (using map()) inside its own Grid container. You need to put the container element outside of the map() function so it can work as you expect.
return (
<div>
<Grid
container
spacing={4}
className={classes.gridContainer}
justify="center"
style={{ marginTop: "80px" }}
>
{getData.map((x) => {
return (
<Grid item xs={12} sm={6} md={4}>
<Card className={classes.root} variant="outlined">
<CardContent>
<Typography
className={classes.title}
color="textSecondary"
gutterBottom
>
Word of the Day
</Typography>
<Typography className={classes.pos} color="textSecondary">
adjective
</Typography>
<Typography variant="body2" component="p">
well meaning and kindly.
<br />
{'"a benevolent smile"'}
</Typography>
</CardContent>
<CardActions>
<Button size="small">Learn More</Button>
</CardActions>
</Card>
</Grid>
);
})}
</Grid>
</div>
);
Related
I can not send currentTodos array to Cardhouse I want to send currentTodos array from seach-result.component.jsx to render loop card-house.component.jsx it show error TypeError: currentTodos.map is not a function Cardhouse
C:/Users/pimdo/Desktop/BF-property/src/components/card-house/card-house.component.jsx:45
seach-result.component.jsx
import React from "react";
import Pagination from '#material-ui/lab/Pagination';
import Typography from '#material-ui/core/Typography';
import Cardhouse from '../card-house/card-house.component.jsx'
class Seachresult extends React.Component {
constructor() {
super();
this.state = {
todos: [1,2, 3, 4, 5, 6, 7, 8, 9, 8, 10],
currentPage: 1,
todosPerPage: 6,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(event, value) {
this.setState({
currentPage: Number(value),
});
}
render() {
const { todos, currentPage, todosPerPage } = this.state;
// Logic for displaying todos
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = todos.slice(indexOfFirstTodo, indexOfLastTodo);
// Logic for displaying page numbers
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(todos.length / todosPerPage); i++) {
pageNumbers.push(i);
}
return (
<div>
<Typography>Page: {currentPage}</Typography>
<Cardhouse currentTodos={currentTodos} />
<Pagination count={pageNumbers.length} page={currentPage} size="large" id={currentPage} onChange={this.handleClick} showFirstButton showLastButton />
</div>
);
}
}
export default Seachresult;
card-house.component.jsx
import React from "react";
import CardMedia from "#material-ui/core/CardMedia";
import CardActionArea from "#material-ui/core/CardActionArea";
import Grid from "#material-ui/core/Grid";
import Container from "#material-ui/core/Container";
import Typography from "#material-ui/core/Typography";
import FavoriteIcon from "#material-ui/icons/Favorite";
import HotelIcon from "#material-ui/icons/Hotel";
import BathtubIcon from "#material-ui/icons/Bathtub";
import SquareFootIcon from "#material-ui/icons/SquareFoot";
import RoomIcon from "#material-ui/icons/Room";
import HouseIcon from "#material-ui/icons/House";
import ShareIcon from "#material-ui/icons/Share";
import IconButton from "#material-ui/core/IconButton";
import { Link } from "react-router-dom";
import Card from "#material-ui/core/Card";
import CardActions from "#material-ui/core/CardActions";
import CardContent from "#material-ui/core/CardContent";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
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,
},
}));
export default function Cardhouse(currentTodos) {
const classes = useStyles();
return (
<Container className={classes.cardGrid} maxWidth="lg">
{/* End hero unit */}
<Grid container spacing={3} style={{ marginTop: 15 }}>
{currentTodos.map(currentTodo=> (
<Grid item key={currentTodo} xs={12} sm={6} md={4}>
<Card className={classes.card}>
<CardActionArea component={Link}
to="/property-detail">
<CardMedia
className={classes.cardMedia}
image="https://source.unsplash.com/random"
title="Image title"
/>
<CardContent className={classes.cardContent}>
<Grid container spacing={1}>
<Grid item xs={12}>
<Typography variant="h5">{"บ้านแสนสุข"}</Typography>
</Grid>
<Grid item xs={12}>
<Typography
variant="subtitle2"
style={{ color: "#969696" }}
>
<RoomIcon fontSize="small" />
{
"ซอย ลาดพร้าว 101 Khlong Chan, Bang Kapi District, Bangkok, Thailand"
}
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="h6" style={{ color: "#26ae61" }}>
{"$"} {"1,900,000"} {"บาท"}
</Typography>
</Grid>
<Grid item xs={3}>
<Typography
variant="subtitle2"
style={{ color: "#969696" }}
>
<HotelIcon fontSize="large" />
</Typography>
</Grid>
<Grid item xs={3}>
<Typography
variant="subtitle2"
style={{ color: "#969696" }}
>
<BathtubIcon fontSize="large" />
</Typography>
</Grid>
<Grid item xs={3}>
<Typography
variant="subtitle2"
style={{ color: "#969696" }}
>
<SquareFootIcon fontSize="large" />
</Typography>
</Grid>
<Grid item xs={3}>
<Typography
variant="subtitle2"
style={{ color: "#969696" }}
>
<HouseIcon fontSize="large" />
</Typography>
</Grid>
<Grid item xs={3}>
<Typography
variant="subtitle2"
style={{ color: "#969696" }}
>
{"ห้องนอน 1"}
</Typography>
</Grid>
<Grid item xs={3}>
<Typography
variant="subtitle2"
style={{ color: "#969696" }}
>
{"น้องน้ำ 1"}
</Typography>
</Grid>
<Grid item xs={3}>
<Typography
variant="subtitle2"
style={{ color: "#969696" }}
>
{"256 ตารางเมตร"}
</Typography>
</Grid>
<Grid item xs={3}>
<Typography
variant="subtitle2"
style={{ color: "#969696" }}
>
{"บ้านเดี่ยว"}
</Typography>
</Grid>
</Grid>
</CardContent>
</CardActionArea>
<CardActions>
<div style={{ marginLeft: "auto" }}>
<IconButton aria-label="share">
<ShareIcon />
</IconButton>
<IconButton aria-label="add to favorites">
<FavoriteIcon />
</IconButton>
</div>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Container>
);
}
What you are passing to Cardhouse is actually a props object, not an array. Thus you are getting this error. Ways to resolve this is to change function Cardhouse argument like:
Using props:
export default function Cardhouse( props )
and then change the map() method to:
{props.currentTodos.map(currentTodo=> (
Using Object Destructuring:
Or, you can simply destructure the props object and you will not have to change anything inside the function like:
export default function Cardhouse({ currentTodos })
For more information: Components and Props
This is because the CardHouse component is treating currentTodos as the prop object instead of a prop attribute.
Change:
export default function Cardhouse(currentTodos) {
// code here
}
into:
export default function Cardhouse({ currentTodos }) {
// code here
}
I am putting together a component for my Goal Sharing social media app. This is what I have so far:
I'm trying to position the Avatar component as well as the two typography components beneath the Avatar component within the center of the left section of this Paper component. I have tried doing this by altering marginLeft and marginTop as you can see in the code below, but the issue when I do this is the components in this Goal component jumble on top of each other when I switch over to smaller devices. So, what's the best way to position these components in the center of the left section, and to ensure they remain that way on smaller devices?
This is the parent component file:
import React, { useEffect } from "react";
import Moment from "react-moment";
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import { getGoals } from "../../actions/goal";
import Spinner from "../layout/Spinner";
import Navbar from "../dashboard/Navbar";
import ThumbUpAltIcon from "#material-ui/icons/ThumbUpAlt";
import ThumbDownAltIcon from "#material-ui/icons/ThumbDownAlt";
import ChatIcon from "#material-ui/icons/Chat";
import DeleteIcon from "#material-ui/icons/Delete";
import DoneIcon from "#material-ui/icons/Done";
import {
Typography,
Container,
CssBaseline,
makeStyles,
Grid,
Card,
Avatar,
CardContent,
CardActions
} from "#material-ui/core";
const useStyles = makeStyles(theme => ({
paper: {
marginTop: theme.spacing(8),
display: "flex",
flexDirection: "column",
alignItems: "center"
},
submit: {
margin: theme.spacing(2, 0, 2)
},
form: {
marginTop: theme.spacing(5)
},
cardGrid: {
paddingTop: theme.spacing(4),
paddingBottom: theme.spacing(4)
},
card: {
height: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center"
},
cardContent: {
flexGrow: 1
},
profileHeader: {
textAlign: "center",
marginBottom: 10
},
avatar: {
width: theme.spacing(10),
height: theme.spacing(10),
marginLeft: "2.5vw",
marginTop: "5vh"
},
name: {
textAlign: "center",
marginLeft: "2vw"
},
goalText: {
marginTop: "5vh",
marginLeft: "3vw"
},
postedOn: {
marginLeft: "2vw"
}
}));
const Goals = ({ getGoals, auth, goal: { goals, user, loading } }) => {
useEffect(() => {
getGoals();
}, [getGoals]);
const classes = useStyles();
return loading ? (
<>
<Navbar />
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Spinner />
</div>
</Container>
</>
) : (
<>
<CssBaseline />
<Navbar />
<main>
<Container className={classes.cardGrid} maxWidth="md">
<Typography variant="h2" className={classes.profileHeader}>
Goals
</Typography>
<Grid container spacing={4}>
{goals.map(singleGoal => (
<Grid item key={singleGoal._id} xs={12}>
<Card fullwidth="true" className={classes.card}>
<Grid container spacing={2}>
<Grid item>
<Avatar
className={classes.avatar}
src={singleGoal.avatar}
/>
<Typography variant="subtitle2" className={classes.name}>
{singleGoal.first_name} {singleGoal.last_name}
</Typography>
<Typography
variant="caption"
className={classes.postedOn}
>
Posted on{" "}
<Moment format="MM/DD/YYYY">{singleGoal.date}</Moment>
</Typography>
</Grid>
<Grid item xs={12} sm container>
<Grid item xs container direction="column" spacing={2}>
<Grid item xs>
<Typography
className={classes.goalText}
variant="body1"
gutterBottom
>
{singleGoal.text}
</Typography>
<Typography variant="h5"></Typography>
</Grid>
</Grid>
</Grid>
</Grid>
<CardContent className={classes.cardContent}></CardContent>
<CardActions>
<ThumbUpAltIcon />
<Typography variant="caption">
{singleGoal.likes.length}
</Typography>
<ThumbDownAltIcon />
<Link to={`/goal/${singleGoal.user}`}>
<ChatIcon />
</Link>
<Typography variant="caption">
{singleGoal.comments.length}
</Typography>
{!auth.loading && singleGoal.user === auth.user._id && (
<DoneIcon />
)}
{!auth.loading && singleGoal.user === auth.user._id && (
<DeleteIcon />
)}
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Container>
</main>
</>
);
};
Goals.propTypes = {
getGoals: PropTypes.func.isRequired,
goal: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
goal: state.goal,
auth: state.auth
});
export default connect(mapStateToProps, { getGoals })(Goals);
Try this
card: {
height: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center"
},
If I click to remove the first like on the first goal, I can see the Redux action firing (using Redux DevTools) for the like being updated. But the number beside is not updating. When I check the console it doesn't appear that anything is refreshing. When I refresh the page, it shows the updated number that was previously liked (the 0 becomes a 1), but I have to refresh the page to see this update. How can I get the number to update in real time without having to refresh the page?
import React, { useEffect } from "react";
import Moment from "react-moment";
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import { addLike, removeLike } from "../../actions/goal";
import { getGoals } from "../../actions/goal";
import Spinner from "../layout/Spinner";
import Navbar from "../dashboard/Navbar";
import ThumbUpAltIcon from "#material-ui/icons/ThumbUpAlt";
import ThumbDownAltIcon from "#material-ui/icons/ThumbDownAlt";
import ChatIcon from "#material-ui/icons/Chat";
import DeleteIcon from "#material-ui/icons/Delete";
import DoneIcon from "#material-ui/icons/Done";
import {
Typography,
Container,
CssBaseline,
makeStyles,
Grid,
Avatar,
Paper,
Button
} from "#material-ui/core";
const useStyles = makeStyles(theme => ({
paper: {
height: "auto",
marginBottom: theme.spacing(3)
},
actionButtons: {
marginTop: "3vh"
},
profileHeader: {
textAlign: "center",
marginBottom: 20
},
avatar: {
width: theme.spacing(7),
height: theme.spacing(7)
}
}));
const Goals = ({
getGoals,
auth,
addLike,
removeLike,
goal: { goals, user, loading }
}) => {
useEffect(() => {
getGoals();
}, [getGoals]);
const classes = useStyles();
return loading ? (
<>
<Navbar />
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Spinner />
</div>
</Container>
</>
) : (
<>
<CssBaseline />
<Navbar />
<main>
<Container>
<Typography variant="h2" className={classes.profileHeader}>
Goals
</Typography>
{/* parent grid */}
<Grid container spacing={4}>
{goals.map(singleGoal => (
<Grid
className={classes.paper}
key={singleGoal._id}
spacing={1}
container
item
direction="row"
alignItems="center"
component={Paper}
>
<Grid
item
container
direction="column"
justify="center"
alignItems="center"
xs={3}
>
<Avatar className={classes.avatar} src={singleGoal.avatar} />
<Typography variant="caption">
{singleGoal.first_name} {singleGoal.last_name}
</Typography>
<Typography variant="caption" className={classes.postedOn}>
Posted on{" "}
<Moment format="MM/DD/YYYY">{singleGoal.date}</Moment>
</Typography>
</Grid>
<Grid container item direction="column" xs={9}>
<Typography variant="body1">{singleGoal.text}</Typography>
<Grid item className={classes.actionButtons}>
<Button size="small" onClick={e => addLike(singleGoal._id)}>
<ThumbUpAltIcon />
</Button>
<Typography variant="caption">
{singleGoal.likes.length}
</Typography>
<Button
size="small"
onClick={e => removeLike(singleGoal._id)}
>
<ThumbDownAltIcon />
</Button>
<Button href={`/goal/${singleGoal._id}`} size="small">
<ChatIcon />
</Button>
{!auth.loading && singleGoal.user === auth.user._id && (
<Button size="small">
<DoneIcon />
</Button>
)}
{!auth.loading && singleGoal.user === auth.user._id && (
<Button size="small">
<DeleteIcon />
</Button>
)}
</Grid>
</Grid>
</Grid>
))}
</Grid>
</Container>
</main>
</>
);
};
Goals.propTypes = {
getGoals: PropTypes.func.isRequired,
goal: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
goal: state.goal,
auth: state.auth
});
export default connect(mapStateToProps, { getGoals, addLike, removeLike })(
Goals
);
I will need to see the rest of your code to be sure, but it seems you are dispatching your action but you are not updating your store, it could be a mistake in your reducer.
hi there I am learning reactjs nowadays and trying to put the main Grid container to the center of viewport, however, it always align from left side of it. I am developing on reactjs with material ui. Also, following this awesome css tutorial https://css-tricks.com/snippets/css/complete-guide-grid/ but no matter what I did none of the center value of any alignment property worked.
import React, { Component } from 'react';
import { withStyles } from '#material-ui/styles';
import ReactHtmlParser from 'react-html-parser';
import axios from 'axios';
import Button from '#material-ui/core/Button';
import KeyboardBackspaceIcon from '#material-ui/icons/KeyboardBackspace';
import Box from '#material-ui/core/Box';
import Grid from '#material-ui/core/Grid';
import Paper from '#material-ui/core/Paper';
import { Redirect } from 'react-router-dom';
import moment from 'moment-timezone';
const styles = {
mainGrid: {
marginTop: "25px",
marginBottom: "100px",
width: "50%",
height: "auto",
borderRadius: "25",
boxShadow: "0",
paddingLeft: "50px",
paddingRight: "50px",
paddingBottom: "50px",
paddingTop: "10px"
}
};
class JobDescriptionComponent extends Component {
constructor(props) {
super(props);
this.state = {
redirect: false,
};
}
render() {
return (
<Grid container>
{ this.state && this.state.jobListing &&
<Grid container direction="column" spacing={1} style={styles.mainGrid}>
<Grid item>
<Button size="small" style={{textTransform: "lowercase", backgroundColor: "white"}} onClick={this.goBackToJobList.bind(this)}>
<KeyboardBackspaceIcon />
back to job list
</Button>
</Grid>
<Grid item style={{fontSize: "18px", marginTop: "10px"}}>
Posted on {moment(this.state.jobListing.posted_time).format('MMM DD YYYY')}
</Grid>
<Grid item style={{fontSize: "25px", marginTop: "10px"}}>
Amazon
</Grid>
<Grid item style={{fontSize: "25px", fontWeight: "bold", textDecoration: "underline"}}>
Software Engineer
</Grid>
<Grid item style={{marginTop: "10px"}}>
<Button variant="outlined" color="inherit" style={{marginRight: "10px", textTransform: "lowercase"}} size="small" clickable>Python</Button>
<Button variant="outlined" color="inherit" style={{marginRight: "10px", textTransform: "lowercase"}} size="small" clickable>Java</Button>
</Grid>
<Grid item style={{marginTop: "20px"}}>
Wirecutter is seeking a full-stack Senior Software Engineer to work collaboratively building digital
products and features that share our research and expertise, helping our millions of readers make informed buying decisions.
</Grid>
<Grid container justify="center" style={{marginTop: "20px"}}>
<Button variant="outlined" color="primary" style={{textTransform: "none"}} size="large">Apply for this job</Button>
</Grid>
</Grid>
}
</Grid>
)
}
}
export default withStyles(styles)(JobDescriptionComponent);
You can use the prop justify="center" at the Grid component.
<Grid container>
{ this.state && this.state.jobListing &&
<Grid container direction="column" spacing={1} style={styles.mainGrid} justify="center">
I create a functional component and a class component.
In the render method I want to call my functional component as a component prop.
My code:
function projectList(props) {
const { classes } = props
return (
<div>
{projects.slice(0, 5).map(project => (
<Card className={classes.card} key={project.id}>
<CardHeader
classes={{ title: classes.h6, subheader:
classes.subtitle1 }}
title={project.projectName}
subheader={project.h5}
/>
<CardActionArea>
<CardMedia
component="img"
alt={project.h5}
className={classes.media}
image={project.img}
/>
<CardContent>
<Typography paragraph>
{project.paragraph}
</Typography>
</CardContent>
</CardActionArea>
<CardActions className={props.classes.actions}>
<Button
className={props.classes.projectButton}
component={Link}
to={project.pathname}
size="medium"
color="secondary"
>
Project Details
</Button>
</CardActions>
</Card>
))}
</div>
)
}
projectList.propTypes = {
classes: PropTypes.any.isRequired, // eslint-disable-line
}
class Projects extends Component {
static propTypes = {
classes: PropTypes.any.isRequired, // eslint-disable-line
}
render() {
const { classes } = this.props
return (
<Grid container className={classes.projectPage}
direction="row">
<Grid item xs />
<Grid item>
<Grid container alignItems="center" direction="column">
{/* Title */}
<Grid item>
<Typography className={classes.h5} variant="h5">Latest
Projects</Typography>
</Grid>
<Grid item xs={12} sm={6} component={projectList} />
<Grid item className={classes.nextButton}>
<Button
className={classes.contained}
size="medium"
variant="contained"
onClick={this.handleShowMore}
>
See More
</Button>
</Grid>
</Grid>
</Grid>
<Grid item xs />
</Grid>
)
}
}
export default withStyles(styles)(Projects)
I have this error message appearing
index.js:1452 Warning: Failed prop type: The prop classes is marked as required in projectList, but its value is undefined.
Anyone could help me fix this ?
Not sure where that <Grid/> component comes from but you could pass an inline wrapper for projectLists and return it as a jsx element (you would need a capital first letter though):
<Grid item xs={12} sm={6} component={() => <ProjectList classes={classes} />} />
Don't forget to change the decleration to capital letter:
function ProjectList(props) {
...