Cant display value from array in graphql query using React - reactjs

Im using React with Apollo on the frontend and i'm having trouble displaying the title and the id of the array likes. Im iterating using the method .map over the tracks array and i can access all the values in it except the values stored in the array likes. Here is the code which explains it better then words i guess :)
App.js
import React from "react";
import withStyles from "#material-ui/core/styles/withStyles";
import { Query } from 'react-apollo'
import { gql } from 'apollo-boost'
import SearchTracks from '../components/Track/SearchTracks'
import TrackList from '../components/Track/TrackList'
import CreateTrack from '../components/Track/CreateTrack'
import Loading from '../components/Shared/Loading'
import Error from '../components/Shared/Error'
const App = ({ classes }) => {
return (
<div className={classes.container}>
<SearchTracks />
<CreateTrack />
<Query query={GET_TRACKS_QUERY}>
{({ data, loading, error }) => {
if (loading) return <Loading />
if (error) return <Error error={error} />
return <TrackList tracks={data.tracks} />
}}
</Query>
</div>
);
};
const GET_TRACKS_QUERY = gql`
query getTracksQuery {
tracks {
id
title
description
url
likes {
title
id
}
postedBy {
id
username
}
}
}
`
const styles = theme => ({
container: {
margin: "0 auto",
maxWidth: 960,
padding: theme.spacing.unit * 2
}
});
export default withStyles(styles)(App);
TrackList.js
import React from "react";
import withStyles from "#material-ui/core/styles/withStyles";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import Typography from "#material-ui/core/Typography";
import ExpansionPanel from "#material-ui/core/ExpansionPanel";
import ExpansionPanelDetails from "#material-ui/core/ExpansionPanelDetails";
import ExpansionPanelSummary from "#material-ui/core/ExpansionPanelSummary";
import ExpansionPanelActions from "#material-ui/core/ExpansionPanelActions";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
import AudioPlayer from '../Shared/AudioPlayer'
import LikeTrack from './LikeTrack'
import CreateTrack from './CreateTrack'
import DeleteTrack from './DeleteTrack'
import UpdateTrack from './UpdateTrack'
import { Link } from 'react-router-dom'
const TrackList = ({ classes, tracks }) => (
<List>
{tracks.map( track => (
<ExpansionPanel key={track.id}>
<ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}>
<ListItem primaryTypographyProps={{
variant: "subheading",
color: "primary"
}} className={classes.root}>
<LikeTrack />
<ListItemText primary={track.title} secondary={
<Link className={classes.link} to={`/profile/${track.postedBy.id}`}>
{track.postedBy.username}
</Link>
} />
<AudioPlayer />
</ListItem>
</ExpansionPanelSummary>
<ExpansionPanelDetails className={classes.details}>
<Typography variant="body1">
{track.description}
{track.likes.id} {/* { Value not displayed } */}
</Typography>
</ExpansionPanelDetails>
<ExpansionPanelActions>
<UpdateTrack />
<DeleteTrack />
</ExpansionPanelActions>
</ExpansionPanel>
) )}
</List>
);
const styles = {
root: {
display: "flex",
flexWrap: "wrap"
},
details: {
alignItems: "center"
},
link: {
color: "#424242",
textDecoration: "none",
"&:hover": {
color: "black"
}
}
};
export default withStyles(styles)(TrackList);

Thanks #xadm use of another map() or single track.likes[0].id works!

Related

setState (In react functional component) is not updating the existing state in react todo app

I have to do app using react and material ui which is combined of these components:
1- App.js
`
import './App.css';
import Header from './components/Header'
import ToDoApp from './components/ToDoApp';
function App() {
return (
<div className="App">
<Header />
<ToDoApp />
</div>
);
}
export default App;
`
2- ToDoApp.js
`
import React from 'react'
import ToDoList from './ToDoList';
import Task from './Task';
import { Container, Grid } from '#mui/material';
import {useState, } from 'react';
export default function ToDoApp() {
const [todos, setTodos] = useState([
{
id: 1,
Title: "Task 1",
done: true
},
{
id: 2,
Title: "Task 2",
done: true
},
])
const deleteTask = (id) => {
// console.log(id);
const newtodos = todos.filter(function(todo) {
return todo.id !== id;
})
setTodos(newtodos);
}
const addTodo = (todoTitle) => {
const lastTodo = todos[todos.length-1];
const newtodosId = lastTodo.id + 1;
const newtodos = todos;
newtodos.push({
id: newtodosId,
title: todoTitle,
done: false,
})
setTodos(newtodos);
console.log(newtodos);
}
return (
<Container maxWidth="sm">
<Grid item xs={12} md={6}>
<ToDoList newTodo={addTodo}/>
<Task ToDoAppList={todos} DeleteTodoTask={deleteTask}/>
</Grid>
</Container>
)
}
`
3- ToDoList.js
`
import * as React from 'react';
import FormControl from '#mui/material/FormControl';
import Typography from '#mui/material/Typography';
import TextField from '#mui/material/TextField';
import IconButton from '#mui/material/IconButton';
import { Add, AddToDriveOutlined } from '#mui/icons-material/';
import { useState } from 'react';
import { useEffect } from 'react';
export default function ToDoList(props) {
const addTodo = props.newTodo
const [todo, setTodo] = useState('')
function writeTodo(e) {
// console.log(e.target.value);
setTodo(e.target.value);
}
const addText = (e) => {
e.preventDefault();
addTodo(todo);
// console.log(todo);
}
return (
<>
<Typography sx={{ mt: 4, mb: 2 }} variant="h6" component="div">
ToDo List
</Typography>
<form onSubmit={(e) => addText(e)}>
<FormControl>
<div
style={{display: 'flex', marginBottom: 20}}>
<TextField
id="standard-helperText"
label="New ToDo"
style={{ width: 450 }}
variant="standard"
value={todo}
onChange={(e) => writeTodo(e)}
/>
<IconButton edge="end" aria-label="create" type="submit">
<Add />
</IconButton>
</div>
</FormControl>
</form>
</>
)
}
`
4-Task.js
`
import React from 'react'
import Paper from '#mui/material/Paper';
import IconButton from '#mui/material/IconButton';
import { Tag, Check, Delete } from '#mui/icons-material';
export default function Task(props) {
const tasks = props.ToDoAppList;
const deleteTask = props.DeleteTodoTask;
const List = tasks.map(task => { return(
<Paper elevation={3} style={{padding: 10, marginTop: 10}} key={task.id}>
<IconButton aria-label="create">
<Tag />
</IconButton>
<span style={{textDecoration: 'line-through'}}>{task.Title}</span>
<IconButton aria-label="delete" style={{float: 'right', color: 'red'}} onClick={()=>deleteTask(task.id)}>
<Delete />
</IconButton>
<IconButton aria-label="check" style={{float: 'right'}}>
<Check />
</IconButton>
</Paper>
)})
return (
<>
{List}
</>
)
}
`
When Submitting the new Todo, the result is not shown on the DOM.
In the ToDoApp.js -> function "addTodo" I used setState to push the new array with the new item, and is sent to ToDoList.js -> form "onSubmit" event in "addText" function. I put the console.log in the function and I am getting the new array but it's not showing on the page.
The tasks listing is handled in the task.js by sending the array through props and using map function to loop over the array.
Note: I handled the delete function in the ToDoApp.js and it's woking with no problem. I don't understand why it's not running with "addTodo" funciton.
First you need to change title to Title (based on your state Schema)
then you dont need to update whole state just add your new task to your current state (function in setTodos)
const addTodo = (todoTitle) => {
const lastTodo = todos[todos.length - 1];
const newtodosId = lastTodo.id + 1;
let newTodo = {
id: newtodosId,
Title: todoTitle,
done: false
};
setTodos((prev)=>[...prev,newTodo])
};

Problem Upgrading React application - material UI v4 to v5

I have this problem upgrading my react application. Basically, I followed the damn mui-v5 documentation, and I still have some erros in my app.tsx file.
import { useReactOidc, withOidcSecure } from '#axa-fr/react-oidc-context';
import { Container } from '#mui/material';
import CssBaseline from '#mui/material/CssBaseline';
import {
ThemeProvider,
StyledEngineProvider,
Theme,
} from '#mui/material/styles';
import createStyles from '#mui/styles/createStyles';
import makeStyles from '#mui/styles/makeStyles';
import { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { Redirect, Route, Switch } from 'react-router-dom';
import { RootState } from 'src/lib/configuration/store-configuration';
import { DarkTheme, LightTheme } from 'src/components/material-ui-theme';
import routes from 'src/components/routes/routes';
import BreadCrumbs from 'src/components/shared/breadcrumbs/breadcrumbs';
import ErrorBoundary from 'src/components/shared/error/Error';
import Footer from 'src/components/shared/footer/footer';
import Header from 'src/components/shared/header/header';
import Notifier from 'src/components/shared/notifications';
import ScrollTop from 'src/components/shared/scroll-top/scroll-top';
import Sider from 'src/components/shared/sider';
import { useTranslation } from 'react-i18next';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
display: 'flex',
},
toolbar: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar,
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
},
container: {
paddingTop: theme.spacing(4),
paddingBottom: theme.spacing(1),
},
}),
);
const App = () => {
const classes = useStyles();
const { oidcUser } = useReactOidc();
const appStore = useSelector((store: RootState) => store.app);
const [drawerOpen, setDrawerOpen] = useState(false);
const handleDrawerToggle = () => {
setDrawerOpen(!drawerOpen);
};
const { i18n } = useTranslation();
useEffect(() => {
document.documentElement.lang = i18n.language;
}, [i18n.language]);
return (
<StyledEngineProvider injectFirst>
<ThemeProvider
theme={
appStore.theme === 'dark' ? DarkTheme : LightTheme
}
>
<div className={classes.root}>
<CssBaseline />
<Header handleDrawerToggle={handleDrawerToggle} />
{oidcUser ? (
<Sider
drawerOpen={drawerOpen}
handleDrawerToggle={
handleDrawerToggle
}
/>
) : null}
<main className={classes.content}>
<div id="back-to-top-anchor" />
<div className={classes.toolbar} />
<Container
maxWidth="xl"
className={classes.container}
>
<Notifier />
<BreadCrumbs />
<ErrorBoundary>
<Switch>
{routes.map(
(route, i) => (
<Route
key={
'route_' +
i
}
exact={
route.exact
}
path={
route.path
}
component={
route.secure
? withOidcSecure(
route.component,
)
: route.component
}
/>
),
)}
<Redirect to="/" />
</Switch>
</ErrorBoundary>
</Container>
<ScrollTop />
<Footer />
</main>
</div>
</ThemeProvider>
</StyledEngineProvider>
);
};
export default App;
Here is the navigator console error :
I tried to modify some imports and used:
import { createTheme } from '#material-ui/core';
const theme = createTheme();
And passing it to makestyles((theme) => ... but nothing changed.
Please guys I need your support ! :)
link for the open-source github project: https://github.com/InseeFr/sugoi-ui

Material-UI Nextjs Integration Theme Toggle

I am attempting to recreate the theme toggle feature that the material-ui website.
My Github Repo: https://github.com/jonnyg23/flask-rest-ecommerce/tree/next-app-migration
So far, I have noticed that material-ui's website uses a cookie called paletteType in order to store the client's theme choice. I understand that a Context Provider should be used to set the cookie, however, my nav-bar implementation has an issue changing themes after the second click of my theme toggle button.
Any help would be greatly appreciated, thank you.
CustomThemeProvider.js:
import React, { createContext, useState } from "react";
import { ThemeProvider } from "#material-ui/core/styles";
import getTheme from "../themes";
import Cookie from "js-cookie";
export const CustomThemeContext = createContext({
// Set the default theme and setter.
appTheme: "light",
setTheme: null,
});
const CustomThemeProvider = ({ children, initialAppTheme }) => {
// State to hold selected theme
const [themeName, _setThemeName] = useState(initialAppTheme);
// Retrieve theme object by theme name
const theme = getTheme(themeName);
// Wrap setThemeName to store new theme names as cookie.
const setThemeName = (name) => {
// console.log("CustomThemeProvider, SetThemeName", name);
Cookie.set("appTheme", name);
_setThemeName(name);
};
const contextValue = {
appTheme: themeName,
setTheme: setThemeName,
};
return (
<CustomThemeContext.Provider value={contextValue}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</CustomThemeContext.Provider>
);
};
export default CustomThemeProvider;
_app.js:
import "../styles/globals.css";
import React, { useContext, useEffect } from "react";
import PropTypes from "prop-types";
import { useAuth0 } from "#auth0/auth0-react";
import Head from "next/head";
import { Provider as NextAuthProvider } from "next-auth/client";
import { makeStyles } from "#material-ui/core/styles";
import CssBaseline from "#material-ui/core/CssBaseline";
import Auth0ProviderWithHistory from "../auth/auth0-provider-with-history";
import CustomThemeProvider, {
CustomThemeContext,
} from "../context/CustomThemeProvider";
export default function App({ Component, pageProps }) {
// const { isLoading } = useAuth0();
const ThemeContext = useContext(CustomThemeContext);
useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector("#jss-server-side");
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
return (
<React.Fragment>
<Head>
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width"
/>
</Head>
<CustomThemeProvider initialAppTheme={ThemeContext.appTheme}>
<Auth0ProviderWithHistory>
<NextAuthProvider session={pageProps.session}>
<CssBaseline />
<Component {...pageProps} />
</NextAuthProvider>
</Auth0ProviderWithHistory>
</CustomThemeProvider>
</React.Fragment>
);
}
App.propTypes = {
Component: PropTypes.elementType.isRequired,
pageProps: PropTypes.object.isRequired,
};
ThemeModeToggle.js:
import React, { useContext } from "react";
import { IconButton } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
import Brightness5TwoToneIcon from "#material-ui/icons/Brightness5TwoTone";
import Brightness2TwoToneIcon from "#material-ui/icons/Brightness2TwoTone";
import { CustomThemeContext } from "../context/CustomThemeProvider";
const useStyles = makeStyles((theme) => ({
light: {
color: theme.palette.secondary.main,
},
dark: {
color: theme.palette.secondary.main,
},
}));
const ThemeModeToggle = ({ fontSize }) => {
const classes = useStyles();
const { appTheme, setTheme } = useContext(CustomThemeContext);
// console.log("ThemeModeToggle", appTheme);
const handleThemeChange = (appTheme, setTheme) => {
if (appTheme === "light") {
setTheme("dark");
} else {
setTheme("light");
}
};
return (
<IconButton onClick={() => handleThemeChange(appTheme, setTheme)}>
{appTheme === "light" ? (
<Brightness5TwoToneIcon fontSize={fontSize} className={classes.light} />
) : (
<Brightness2TwoToneIcon fontSize={fontSize} className={classes.dark} />
)}
</IconButton>
);
};
export default ThemeModeToggle;
Solved, I found the issue!
It turns out that my page components such as StorefrontIcon and the AppBar colors had to be explicitly added with className={classes.NAME} or with style={{ backroundColor: theme.palette.primary.main }} for example. Both methods are shown below in my nav-bar.js file.
import React from "react";
import { AppBar, Grid, Container, Toolbar } from "#material-ui/core";
import { makeStyles, useTheme } from "#material-ui/core/styles";
import StorefrontIcon from "#material-ui/icons/Storefront";
import MainNav from "./main-nav";
import AuthNav from "./auth-nav";
import ThemeModeToggle from "./ThemeModeToggle";
import { Desktop, SmallScreen } from "./Responsive";
import HamburgerMenu from "./HamburgerMenu";
const useStyles = makeStyles((theme) => ({
root: {
[theme.breakpoints.down("sm")]: {
padding: 0,
},
},
icon: {
color: theme.palette.primary.contrastText,
},
}));
const NavBar = () => {
const classes = useStyles();
const theme = useTheme();
return (
<AppBar
position="static"
elevation={3}
style={{ backgroundColor: theme.palette.primary.main }}
>
<Toolbar>
<Container className={classes.root}>
<Grid container justify="center" alignItems="center" spacing={2}>
<Desktop>
<Grid item xs={1}>
<StorefrontIcon fontSize="large" className={classes.icon} />
</Grid>
<Grid item xs={7}>
<MainNav />
</Grid>
<Grid item xs={3}>
<AuthNav />
</Grid>
<Grid item xs={1}>
<ThemeModeToggle fontSize="large" />
</Grid>
</Desktop>
<SmallScreen>
<Grid item xs={2}>
<StorefrontIcon fontSize="medium" className={classes.icon} />
</Grid>
<Grid container item xs={10} justify="flex-end">
<HamburgerMenu />
</Grid>
</SmallScreen>
</Grid>
</Container>
</Toolbar>
</AppBar>
);
};
export default NavBar;

Overriding Material UI Badge style is not working

I am trying to translate and position the Badge component by overriding the root CSS style, but for some reason it's not taking anything into effect. My Card component seems to be seeing the styling via the className prop, but I for some reason I the Badge component isn't seeing anything. I am following the documentation here.
Here's my component:
import React, { useState } from "react";
import PropTypes from "prop-types";
import Card from "#material-ui/core/Card";
import { Badge } from "#material-ui/core";
import CardHeader from "#material-ui/core/CardHeader";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles(theme => ({
root: {
anchorOriginTopRightRectangle: {
transform: "translate(-100%, -50%)"
}
},
card: {
maxWidth: 345
},
}));
const CardItem = ({
name,
discount
}) => {
const classes = useStyles();
return (
<Card className={classes.card}>
<CardHeader
title={
<>
{name}
<Badge
badgeContent={`-10%`}
color="error"
></Badge>
</>
}
subheader={"$1234"}
/>
// ... Card content
</Card>
);
};
export default(CardItem);
import React, { useState } from "react";
import PropTypes from "prop-types";
import Card from "#material-ui/core/Card";
import { Badge } from "#material-ui/core";
import CardHeader from "#material-ui/core/CardHeader";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles(theme => ({
anchorTopRight: {
transform: "translate(-100%, -50%)"
},
card: {
maxWidth: 345
},
}));
const CardItem = ({
name,
discount
}) => {
const classes = useStyles();
return (
<Card className={classes.card}>
<CardHeader
title={
<>
{name}
<Badge
classes={{ anchorOriginTopRightRectangle: classes.anchorTopRight}} // <== Working Code
badgeContent={`-10%`}
color="error"
></Badge>
</>
}
subheader={"$1234"}
/>
// ... Card content
</Card>
);
};
export default(CardItem);

Button with Icon component

I have a <Button /> component and an <Icon/> component.
I try to implement a button with an icon.
The Button.jsx story:
import React from "react";
import { storiesOf } from "#storybook/react";
import Button from "../components/Button";
import Icon from "../components/Icon/Index";
import { iconTypes } from "../components/Icon/Index";
storiesOf("Button/Primary", module)
.add("With icon", () => (
<Button><Icon type={iconTypes.arrowRight}/></Button>
))
That works fine but I would like the api for a Button with an icon to be-
<Button icon={icons.arrow}>Click Me</Button>
How can I achieve that?
The Icon.jsx story:
import React from "react";
import { storiesOf } from "#storybook/react";
import Icon from "../components/Icon/Index";
import { iconTypes } from "../components/Icon/Index";
storiesOf("Icon", module)
.add("Arrow Right", () => (
<Icon type={iconTypes.arrowRight}>
</Icon>
))
.add("Arrow Left", () => (
<Icon type={iconTypes.arrowLeft}>
</Icon>
));
The <Button /> component:
import React from 'react';
import { css, cx } from 'emotion';
import colors from '../styles/colors';
export default function Button({
children,
...props
}) {
const mergedStyles = cx(
// css
);
// other css stuff
return (
<button {...props} disabled={disabled} className={mergedStyles}>
{children}
</button>
);
And <Icon /> component:
import React from "react";
import { css } from 'emotion';
import ArrowRight from "./arrow-right2.svg";
import ArrowLeft from "./arrow-left2.svg";
export const iconTypes = {
arrowRight: 'ARROW_RIGHT',
arrowLeft: 'ARROW_LEFT',
}
const iconSrc = {
ARROW_RIGHT: ArrowRight,
ARROW_LEFT: ArrowLeft,
}
const circleStyles = css({
width: 24,
height: 24,
borderRadius: "50%",
backgroundColor: "#f7f7f9",
display: "flex",
justifyContent: "center"
});
export default function Icon({ type }) {
return (
<div className={circleStyles}>
<img src={iconSrc[type]} />
</div>
)
};
Any help would be appreciated.
import React from 'react';
import {css, cx} from 'emotion';
import colors from '../styles/colors';
//import your ICON component & make sure your path is right
import Icon from "./../Icon";
export default function Button({
children,
disabled,
icon,
...props
}) {
const mergedStyles = cx(//your css);
return (
<button {...props} disabled={disabled} className={mergedStyles}>
// If icon prop is provided then render ICON component
{icon && <Icon type={icon}/>}
//Other children
{children}
</button>
);
}
in render of Button, you can do something like that :
Button.js:
render(){
const { icon } = this.props
return(
<Button>
{icon && <Icon type={icon}/>}
<Button>
)
}

Resources