I am using ChakraUI and React to make a project where I want to show a list of members in the form of cards.
But for some reason the component is not rendering properly. Only the Team member text is showing in the browser. But the part where I mapped the listOfTeamMembers array, is not rendering. What is the reason behind it?
Member component
import { Box, Text, Center, Stack } from "#chakra-ui/layout";
import listOfTeamMembers from "./team-members-list";
function TeamMembers() {
return (
<Box id='team-members' mt={10}>
<Box>
<Center>
<Text fontSize='4xl' as='u' fontFamily='sans-serif'>
Team Members //only this part is showing on website
</Text>
</Center>
<Box>
{listOfTeamMembers.map((items) => {
<Stack
spacing={{ base: "18px", md: "25px", lg: "40px" }}
direction='row'
>
<Box
w={{ base: "300px", md: "200px", lg: "100px" }}
h={{ base: "300px", md: "200px", lg: "100px" }}
borderRadius={5}
>
{items.name}
{items.contact}
{items.details}
</Box>
</Stack>;
})}
</Box>
</Box>
</Box>
);
}
export default TeamMembers;
The member array
const listOfTeamMembers = [
{
name: "Dr. DoofenShmirtz",
contact: "#",
details:
"The evil genius and a loving father of one daughter. Hate Perry the platipus",
},
{
name: "Dr. Frankestein",
contact: "#",
details: "I experiment on Dead People",
},
{
name: "Dr. Drake Ramoray",
contact: "#",
details: "Has magical hands. Can do brain Transplant without any Help",
},
{
name: "Dr. House MD",
contact: "#",
details: "I am a fictional Character. What am I doing here!",
},
{
name: "Dr. Hibbert",
contact: "#",
details: "ohhohohohohohoho",
},
{
name: "Dr. Elmer Hartman",
contact: "#",
details: "My role as a doctor is to cure people. But I do the opposite.",
},
];
export default listOfTeamMembers;
My App.js component
import { ChakraProvider } from "#chakra-ui/provider";
import { useState } from "react";
import Navbar from "../src/components/navbar";
import Home from "./components/Home";
import theme from "./theme";
import TeamMembers from "../src/components/Team Members/team-members";
function App() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
<ChakraProvider theme={theme}>
<Navbar isMenuOpen={isMenuOpen} setIsMenuOpen={setIsMenuOpen} />
<Home isMenuOpen={isMenuOpen} />
<TeamMembers />
</ChakraProvider>
);
}
export default App;
You have to return array item also :-)
{listOfTeamMembers.map((items) => {
return (
<Stack
spacing={{ base: "18px", md: "25px", lg: "40px" }}
direction="row"
>
<Box
w={{ base: "300px", md: "200px", lg: "100px" }}
h={{ base: "300px", md: "200px", lg: "100px" }}
borderRadius={5}
>
{items.name}
{items.contact}
{items.details}
</Box>
</Stack>
);
})}
Here is demo: https://codesandbox.io/s/sharp-ganguly-lcj38?file=/src/App.js:1122-1694
Related
I have the following component:
import ListItemButton from '#mui/material/ListItemButton';
import ListItemIcon from '#mui/material/ListItemIcon';
import Tooltip from '#mui/material/Tooltip';
const MenuItem = ({data, onClick}) => {
const menuItemOnClick = () => {
onClick(data);
};
const style = {
display: "flex",
justifyContent: "center",
color: "white",
margin:"auto"
};
return (
<Tooltip title={data.friendlyName} placement="right">
<ListItemButton onClick={menuItemOnClick}>
<ListItemIcon sx={style}>
{data.icon}
</ListItemIcon>
</ListItemButton>
</Tooltip>
);
};
export default MenuItem;
The prop is being passed the following way:
myArray.map(entityTypeData => <MenuItem key={entityTypeData.name} data = {entityTypeData} onClick={onClick} />)
myArray is defined like this:
return [
{
name: "X",
friendlyName: "X",
icon: <MedicationIcon />,
entities: []
},
{
name: "Y",
friendlyName: "Y",
icon: <BiotechIcon />,
entities: []
},
...
];
Further information:
Preferably I want to avoid styling my icon where the array is declared, as this is something for which I want to enforce only when the MenuItem Component is used.
I tried changing the size of the parents, but it doesn't cascade to the child icon.
How can I change the height/width of the icon received as props?
It's very similar of what you already did for styling the <ListItemIcon> but you need to do it on the Icon it self. For example look at the code bellow:
Create the style object:
const iconStyle = {
fontSize: "40px"
};
And in your myArray apply the style using the sx prop
return [
{
name: "X",
friendlyName: "X",
icon: <MedicationIcon sx={iconStyle} />,
entities: []
},
{
name: "Y",
friendlyName: "Y",
icon: <BiotechIcon sx={iconStyle} />,
entities: []
},
...
];
UPDATED
Or based on your comment you can use the existing styling for the <ListItemIcon> like this:
const style = {
display: "flex",
justifyContent: "center",
color: "red",
margin: "auto",
"& svg": {
fontSize: "40px",
}
};
Here is a working codesandbox with an example
this is my code:
import React, { Component } from "react";
import MobileDetect from "mobile-detect";
import { map, orderBy, flowRight as compose, isEmpty, get } from "lodash";
import {
Grid,
List,
Avatar,
ListItem,
Typography,
ListItemSecondaryAction,
withStyles
} from "#material-ui/core";
import PageSubTitle from "./PageSubTitle";
import NotificationBar from "./NotificationBar";
const style = (theme) => ({
highlightedText: {
cursor: "pointer"
},
logoAvatar: {
width: 60,
height: 60,
borderRadius: "5%",
background: theme.palette.common.white,
[theme.breakpoints.down("xs")]: {
width: 40,
height: 40
}
},
logoImg: {
width: "100%",
height: "unset"
},
listItemRoot: {
"&:hover": {
boxShadow: "0px 4px 10px rgba(0, 122, 255, 0.25)"
}
},
listItemText: {
padding: "0 16px"
},
goToPortal: {
display: "flex",
"vertical-align": "middle",
"justify-content": "center",
"align-items": "center",
[theme.breakpoints.down("xs")]: {
"justify-content": "start"
}
},
inline: {
display: "flex",
[theme.breakpoints.down("xs")]: {
"font-size": "10px"
}
}
});
const depositVendors = [
{
skrill: {
value: "skrill",
acronym: "SKR",
label: "Skrill",
isRealDeposit: true,
manuallyCreated: false,
clientPermitted: true,
supportCurrencyConverstation: false,
allowCompleteRejected: true, // skrill can reject a transaction first and then complete it
fundingCategory: "eWallet",
localization: { id: "", defaultMessage: "" }
}
}
];
const countryPaymentOptions = [
{
id: 25,
country: "_default",
paymentOption: {
id: 25,
name: "Klarpay",
provider: "klarpay",
logoUrl:
"https://bdswiss-dev.s3-eu-west-1.amazonaws.com/payment-method-logos/astropay/1543430750284",
localizationKey: "",
paymentKey: "ob-sandbox-natwest",
additionalFields: "{}",
bankDetails: [],
ccType: null,
processingTime: null,
processingTimeValue: null,
minMaxAmount: null,
transactionFee: null,
__typename: "PaymentOptionType"
},
enabled: true,
rank: 0,
popular: false,
__typename: "CountryPaymentOptionType"
}
];
const account = {
__typename: "ForexMauritiusAccount",
id: 22,
currency: "USD",
balance: 0,
isDemo: false,
hidden: false,
remoteId: "6224776",
availableWithdrawalOptions: [
"neteller",
"skrill",
"bankTransfer",
"payRetailers",
"BTC"
],
withdrawals: [],
minimumDeposit: 10,
accountName: null,
minimumPartialDeposit: 0,
minimumBonusDeposit: 10,
isArchived: false,
isViewOnly: false,
accountSubtype: "basic",
isReadOnly: true,
freeMargin: 0,
withdrawalsBlocked: false,
copyTrading: null,
eligibleBonus: {
eligibleBonus: true,
accountTypeBonus: "basic",
daysLeft: 26,
percentage: 0.3,
maxAmount: 500,
terms:
"https://bdswiss.s3.eu-central-1.amazonaws.com/BDSwiss_Bonus_TC_BDSMarkets.pdf",
__typename: "eligibleBonusAccountTypeType"
},
swapFree: false
};
const getFormattedAmount = ({ currency, amount, locale }) => {
return Intl.NumberFormat(locale || {}, {
style: "currency",
currency,
minimumFractionDigits: 2
}).format(amount);
};
function isMobile() {
const md = new MobileDetect(window.navigator.userAgent);
return !!md.mobile() || !!md.versionStr("Mobile");
}
export class PaymentMethodSelection extends Component {
render() {
const {
locale,
classes,
onPaymentMethodSelected,
selectedPaymentMethodId
} = this.props;
const formattedAmount = getFormattedAmount({
amount: 100,
currency: "EUR",
locale: "en"
});
return (
<Grid container spacing={!isMobile() ? 3 : 0}>
<Grid item xs={12}>
<PageSubTitle>
<Typography>Select deposit method for</Typography>
</PageSubTitle>
<NotificationBar status="info" noMargin>
<Typography variant="body1">{formattedAmount}</Typography>
</NotificationBar>
</Grid>
<Grid item xs={12}>
<List>
{map(
orderBy(countryPaymentOptions, (opt) => opt.rank, ["desc"]),
(opt, i) => {
const { paymentOption } = opt;
const transactionFee = get(paymentOption, "transactionFee");
const minPaymentOptionAmount = getFormattedAmount({
amount: 100,
currency: account.currency,
locale
});
const maxPaymentOptionAmount = getFormattedAmount({
amount: get(paymentOption, "minMaxAmount"),
currency: account.currency,
locale
});
const feesPaymentOptionAmount =
transactionFee === "No Fees"
? "No Fees"
: getFormattedAmount({
amount: 100,
currency: "EUR",
locale: "en"
});
let paymentOptionName;
if (isEmpty(paymentOption.localizationKey)) {
paymentOptionName =
paymentOption.name ||
depositVendors[paymentOption.provider].label;
} else {
if (
depositVendors[paymentOption.provider].label ===
depositVendors[paymentOption.provider].localization.t(
locale
)
) {
paymentOptionName =
paymentOption.name ||
depositVendors[paymentOption.provider].label;
} else {
paymentOptionName = depositVendors[
paymentOption.provider
].localization.t(locale);
}
}
return (
<React.Fragment key={i}>
<ListItem
classes={{ root: classes.listItemRoot }}
key={i}
button
onClick={(e) => onPaymentMethodSelected(opt.id)}
>
<Grid
container
justifyContent="space-between"
spacing={1}
>
<Grid item xs={2} className={classes.goToPortal}>
<Grid container>
<Grid item xs={12}>
<Avatar
alt={paymentOption.name}
src={paymentOption.logoUrl}
className={classes.logoAvatar}
imgProps={{ className: classes.logoImg }}
/>
</Grid>
</Grid>
</Grid>
<Grid item xs={9}>
<Grid
container
direction="row"
justifyContent="flex-start"
alignItems="center"
spacing={2}
style={{ color: "blue" }}
>
<Grid item xs={12}>
<Typography
variant="body2"
className={classes.inline}
>
{paymentOptionName}
</Typography>
</Grid>
{get(paymentOption, "processingTime") && (
<Grid item>
<Grid container>
{/* <Grid item>
<img alt="test" />
</Grid> */}
<Grid item>
<Typography
variant="body1"
className={classes.inline}
>
10 days
</Typography>
</Grid>
</Grid>
</Grid>
)}
<Grid item>
<Grid container>
{/* <Grid item>
<img alt="test" />
</Grid> */}
<Grid item>
<Typography
variant="body1"
className={classes.inline}
>
{" "}
{minPaymentOptionAmount} -{" "}
{maxPaymentOptionAmount}{" "}
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item>
<Grid container>
{/* <Grid item>
<img alt="test" />
</Grid> */}
<Grid item>
<Typography
variant="body1"
className={classes.inline}
>
{" "}
{feesPaymentOptionAmount}{" "}
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={1} className={classes.goToPortal}>
<Grid container>
<Grid item xs={12}>
{opt.id === selectedPaymentMethodId && (
<ListItemSecondaryAction>
<img alt="test" />
</ListItemSecondaryAction>
)}
</Grid>
</Grid>
</Grid>
</Grid>
</ListItem>
</React.Fragment>
);
}
)}
</List>
</Grid>
</Grid>
);
}
}
export default compose(withStyles(style))(PaymentMethodSelection);
this is the result without hover:
this is with hover so far:
this is how I want it to look:
Hello guys, I am trying to add some hover effect over a container using MUI grids and typography. There are 3 elements on my main grid. The Title, the image and the US Currency line. I have managed to add the hover effect on the whole container but I cant seem to be able to add the text colour change while on hover. I want the us currency line to change to colour black on hover.
I don't know know CSS or React style object very well but I managed to get the CSS to do what you want. Then I searched for a tool that converts CSS to React style object.
With CSS if you want to change the color of a specific text when you hover to a container:
.container:hover .text {
color: black;
}
Here is an example
After using the converter tool the React inline object:
{
"container_hover__text": {
"color": "black"
}
}
I am using material ui and reactjs for having grouped autocomplete.
Below is the code.
import * as React from "react";
import TextField from "#mui/material/TextField";
import Autocomplete from "#mui/material/Autocomplete";
import Typography from "#material-ui/core/Typography";
export default function Grouped() {
top100Films = top100Films.sort((a, b) =>
a.genre.toLowerCase() > b.genre.toLowerCase() ? 1 : -1
);
return (
<Autocomplete
id="grouped-demo"
options={top100Films}
groupBy={(option) => option.genre}
getOptionLabel={(option) => option.title}
sx={{ width: 300 }}
renderInput={(params) => (
<TextField {...params} label="With categories" />
)}
/>
);
}
// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
let top100Films = [
{ title: "The Shawshank Redemption", genre: "thriller" },
{ title: "The Dark Knight", genre: "super hero" },
{ title: "The Godfather: Part II", genre: "thriller" },
{ title: "12 Angry Men", genre: "war" },
{ title: "Schindler's List", genre: "war" },
{ title: "superman", genre: "super hero" },
{ title: "The Godfather", genre: "thriller" },
{
title: "The Lord of the Rings: The Return of the King",
genre: "adventure"
}
];
Below is the link to codesandbox
https://codesandbox.io/s/fontsize-8n6ovr?file=/demo.js:0-1195
I only want to change font size of groupBy and options text, without changing the size of input box.
Tried using Typography but wasn't able to achieve it. Please help me do it.
Thanks in advance!
You could use renderGroup prop
The prop type is function whose signature is
function(params: AutocompleteRenderGroupParams) => ReactNode
And according to the type definition of AutocompleteRenderGroupParams, we could know the properties of params
export interface AutocompleteRenderGroupParams {
key: string;
group: string;
children?: React.ReactNode;
}
From all the given, we could customize the group render accordingly
renderGroup={(params) => (
<Box key={params.key}>
<Typography fontSize={20} fontStyle="italic" p={1}>
{params.group}
</Typography>
{params.children}
</Box>
)}
Codesandbox Demo
References
Autocomplete API, where we could find detail info for renderGroup prop
Autocomplete.d.ts, where we could find type definition for AutocompleteRenderGroupParams
#hgb123 thank you for helping.
Extending his answer, Below is the code for both options and groupBy text including class based and hooks based component
class based component
import * as React from "react";
import TextField from "#mui/material/TextField";
import Autocomplete from "#mui/material/Autocomplete";
import Typography from "#mui/material/Typography";
import Box from "#mui/material/Box";
import { withStyles } from "#material-ui/core/styles";
const useStyles = (theme) => ({
option: {
fontSize: "20px"
}
});
class Grouped extends React.Component {
top100Films = top100Films.sort((a, b) =>
a.genre.toLowerCase() > b.genre.toLowerCase() ? 1 : -1
);
render() {
const { classes } = this.props;
return (
<Autocomplete
classes={{ option: classes.option }}
id="grouped-demo"
options={top100Films}
groupBy={(option) => option.genre}
getOptionLabel={(option) => option.title}
sx={{ width: 300 }}
renderInput={(params) => (
<TextField {...params} label="With categories" />
)}
renderGroup={(params) => (
<Box key={params.key}>
<Typography fontSize={40}>{params.group}</Typography>
{params.children}
</Box>
)}
/>
);
}
}
// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
let top100Films = [
{ title: "The Shawshank Redemption", genre: "thriller" },
{ title: "The Dark Knight", genre: "super hero" },
{ title: "The Godfather: Part II", genre: "thriller" },
{ title: "12 Angry Men", genre: "war" },
{ title: "Schindler's List", genre: "war" },
{ title: "superman", genre: "super hero" },
{ title: "The Godfather", genre: "thriller" },
{
title: "The Lord of the Rings: The Return of the King",
genre: "adventure"
}
];
export default withStyles(useStyles)(Grouped);
// incase of redux
// import { compose } from 'redux';
// export default compose(connect(mapStateToProps, mapDispatchToProps),withStyles(useStyles))(Grouped)
Hooks based component
import * as React from "react";
import TextField from "#mui/material/TextField";
import Autocomplete from "#mui/material/Autocomplete";
import Typography from "#mui/material/Typography";
import Box from "#mui/material/Box";
import { makeStyles } from "#material-ui/core/styles";
useStyles = makeStyles(() => ({
// stands for .MuiAutocomplete-option
option: {
fontSize: "20px"
}
}));
export default function Grouped() {
top100Films = top100Films.sort((a, b) =>
a.genre.toLowerCase() > b.genre.toLowerCase() ? 1 : -1
);
const classes = useStyles();
return (
<Autocomplete
classes={{ option: classes.option }}
id="grouped-demo"
options={top100Films}
groupBy={(option) => option.genre}
getOptionLabel={(option) => option.title}
sx={{ width: 300 }}
renderInput={(params) => (
<TextField {...params} label="With categories" />
)}
renderGroup={(params) => (
<Box key={params.key}>
<Typography fontSize={40}>
{params.group}
</Typography>
{params.children}
</Box>
)}
/>
);
}
// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
let top100Films = [
{ title: "The Shawshank Redemption", genre: "thriller" },
{ title: "The Dark Knight", genre: "super hero" },
{ title: "The Godfather: Part II", genre: "thriller" },
{ title: "12 Angry Men", genre: "war" },
{ title: "Schindler's List", genre: "war" },
{ title: "superman", genre: "super hero" },
{ title: "The Godfather", genre: "thriller" },
{
title: "The Lord of the Rings: The Return of the King",
genre: "adventure"
}
];
Below is the link to codesandbox
https://codesandbox.io/s/fontsize-forked-qhyvkn?file=/demo.js
We can also do it without styles (withStyles, useStyles) by removing classes={{ option: classes.option }} from Autocomplete and its related code from the file and adding Typography in renderGroup <Typography fontSize={40}> {params.children}<Typography fontSize={40}>
renderGroup={(params) => (
<Box key={params.key}>
<Typography fontSize={40}>
{params.group}
</Typography>
<Typography fontSize={40}> //adding this
{params.children}
<Typography fontSize={40}>
</Box>
)}
/>
Hope this helps someone!
This is the array of objects that I want to map through. As you see, is also contains material-ui icon components. This is why I just can't map through the translation.json in locales
const sidebarData = [
{
text: "Picken",
icon: <GetAppIcon style={{ fontSize: 35 }} />,
path: "/picken",
title: "Artikel picken",
},
{
text: "Hole Artikel",
icon: <AddShoppingCartIcon style={{ fontSize: 35 }} />,
path: "/holeartikel",
title: "Hole Artikel zu dieser Workstation",
},
{
text: "Hole Regal",
icon: <ImportExportIcon style={{ fontSize: 35 }} />,
path: "/holeregal",
title: "Hole Regal zu dieser Workstation",
}];
The locales for the english translation (locales/en/translation.json) looks like that:
{
"sidebarmenu": {
"jobliste": "Joblist",
"picken": "Picken",
"artikel": "Get Item",
"regal": "Get Pod",
"leer": "Get Storage",
"karte": "Show Map"
},
"header": {
"jobliste": "Joblist",
"picken": "Pick Item",
"artikel": "Get Item to this workstation",
"regal": "Get Pod to this workstation",
"leer": "Get empty storage to this workstation",
"karte": "Show Warehouse Map"
}
And the component looks like this:
import React, { useState, useEffect} from "react";
import { useHistory } from "react-router-dom";
import layouttheme from "./layouttheme";
import ListAltIcon from "#material-ui/icons/ListAlt";
import {Drawer,Typography,Divider,ListItemText,ListItem,List,Box,} from "#material-ui/core";
import useStyles from "./LayoutStyles";
import {useTranslation } from "react-i18next";
function DrawerComp({ open, setOpen, sidebarData }) {
const classes = useStyles();
const history = useHistory();
const [getPath, setGetPath] = useState("Jobliste");
const { t } = useTranslation();
function handleClick(item) {
history.push(item.path);
setGetPath(item.title);
}
return (
<Drawer variant="persistent" anchor="left" open={open}>
<List className={classes.sbList} sidebarData={sidebarData}>
{sidebarData.map(item => (
<ListItem
key={item.text}
onClick={() => handleClick(item)}
>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText>{item.text}</ListItemText>
</ListItem>
))}
</List>
</Drawer>
);
}
export default DrawerComp;
So my question is how can connect the sidebarData with the translation.json in order to get the translation?
You can either translate the sidebarData if this object is composed in a parent component:
const sidebarData = [{
text: t("picken"),
icon: <GetAppIcon style={{ fontSize: 35 }} />,
path: "/picken",
title: "Artikel picken",
}, {
text: t("hole_artikel"),
icon: <AddShoppingCartIcon style={{ fontSize: 35 }} />,
path: "/holeartikel",
title: "Hole Artikel zu dieser Workstation",
}, {
text: t("hole_regal"),
icon: <ImportExportIcon style={{ fontSize: 35 }} />,
path: "/holeregal",
title: "Hole Regal zu dieser Workstation",
}];
You can also use the translation keys in the object instead of the full text. Then in your render function, wrap the properties with t().
I personally don't like doing this, since you can't extract the translations with tools like i18next-scanner.
const sidebarData = [{
text: "picken",
icon: <GetAppIcon style={{ fontSize: 35 }} />,
path: "/picken",
title: "Artikel picken",
}, {
text: "hole_artikel",
icon: <AddShoppingCartIcon style={{ fontSize: 35 }} />,
path: "/holeartikel",
title: "Hole Artikel zu dieser Workstation",
}, {
text: "hole_regal",
icon: <ImportExportIcon style={{ fontSize: 35 }} />,
path: "/holeregal",
title: "Hole Regal zu dieser Workstation",
}];
return (
<Drawer variant="persistent" anchor="left" open={open}>
<List className={classes.sbList} sidebarData={sidebarData}>
{sidebarData.map(item => (
<ListItem
key={item.text}
onClick={() => handleClick(item)}
>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText>{t(item.text)}</ListItemText>
</ListItem>
))}
</List>
</Drawer>
);
I cannot seem to shake this error and I don't know what I'm doing wrong. I have a 'show' screen displaying a selected post. At the top of the screen is a profile picture for the user that posted.
When the component is mounted, details of the select post are retrieved and stored in a state ("post"). This post-state includes a user object which contains information about the user that made the post - including, in some cases, their profile picture.
As not all users have profile pictures, I use a simple ternary operator to determine whether a profile picture or an icon should be displayed:
const [post, setPost] = useState({})
useEffect(() => {
const post = feed.find(el => el.id === props.route.params.postId)
setPost(post)
}, [feed])
return <>
<ListItem>
{ post.user.profilePic
? <Avatar source={{ uri: post.user.profilePic }} size="medium" rounded />
: <Avatar rounded icon={{ name: 'person', type: "ionicons" }} size="medium" rounded overlayContainerStyle={{ backgroundColor: 'grey' }} />}
<ListItem.Content>
<ListItem.Title style={{ fontWeight: "bold" }}>{"#" + post.user.userName}</ListItem.Title>
</ListItem.Content>
</ListItem>
Quite straightforward and this same code works fine in other components, but in this one, it keeps crashing:
TypeError: undefined is not an object (evaluating 'post.user.profilePic')
This error is located at:
in ShowScreen (at SceneView.tsx:122)
in StaticContainer
in StaticContainer (at
etc
I have tried returning a loading component if the post hasn't fully loaded; change the ternary operator to check if post. user.profile pic === undefined; using type and hasOwnProperty methods.
Here is the 'post' as console.logged:
Object {
"caption": "Testing caption
",
"comments": Array [
Object {
"authorId": "vqEzix1k4tQer7wmMG32Fby4KKg1",
"authorName": "sarah123",
"comment": "Comments test",
"created": t {
"nanoseconds": 119000000,
"seconds": 1614335984,
},
"id": "sYCm2nk0XpDKYiBQ6fmy",
},
Object {
"authorId": "vqEzix1k4tQer7wmMG32Fby4KKg1",
"authorName": "rob123",
"comment": "Testing comments works!",
"created": t {
"nanoseconds": 362000000,
"seconds": 1614331033,
},
"id": "srd6faF3uw2AvQbWskzd",
},
],
"creation": t {
"nanoseconds": 144000000,
"seconds": 1612301025,
},
"currentUserLike": false,
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/rn-instagram-clone-88f85.appspot.com/o/post%2F2qZfG0Wve6hmffdS0hL13qoIYp92%2F0.xgqi91loct?alt=media&token=ac9bc56f-d04b-46f2-acc6-45f8475134ab",
"id": "PiFmGtUb55OMGUiIQO61",
"location": "Bangor",
"user": Object {
"email": "test#gmail.com",
"name": "test",
"uid": "2qZfG0Wve6hmffdS0hL13qoIYp92",
},
}
What am I doing wrong? Thanks.
You should update post state when you have found the valid one.
const [post, setPost] = useState({})
useEffect(() => {
const post = feed.find(el => el.id === props.route.params.postId)
if (post) {
setPost (post)
}
}, [feed])
return <>
<ListItem>
{ post.user.profilePic
? <Avatar source={{ uri: post.user.profilePic }} size="medium" rounded />
: <Avatar rounded icon={{ name: 'person', type: "ionicons" }} size="medium" rounded overlayContainerStyle={{ backgroundColor: 'grey' }} />}
<ListItem.Content>
<ListItem.Title style={{ fontWeight: "bold" }}>{"#" + post.user.userName}</ListItem.Title>
</ListItem.Content>
</ListItem>
Try this way
<ListItem>
{post && post.user && post.user.profilePic ? (
<Avatar source={{ uri: post.user.profilePic }} size="medium" rounded />
) : (
<Avatar
rounded
icon={{ name: "person", type: "ionicons" }}
size="medium"
rounded
overlayContainerStyle={{ backgroundColor: "grey" }}
/>
)}
<ListItem.Content>
<ListItem.Title style={{ fontWeight: "bold" }}>
{post && post.user && post.user.userName ? "#" + post.user.userName :""}
</ListItem.Title>
</ListItem.Content>
</ListItem>
use null check post.user && post.user.profilePic