How to use MUI v5+ with class-based react components? - javascript

We are trying to update our web application from material ui (v4) to mui (v5)
Using the available examples, we have managed for function based components, but it does not seem to work for class based components, and there is very little examples around on class based components and MUI.
Out pattern looks something like the below currently, but none of the styles are applied to the MUI components. The same pattern seems to work on a POC project with functional components.
The container file Login.js
import {connect} from 'react-redux'
import Layout from '../pages/login.js';
import { call } from '../reducers/helloWorld'
import { logIn, logOut } from "../reducers/userManagement"
const mapStateToProps = state => ({
user: state.userManagement.user,
requestState: state.helloWorld.requestState,
})
const mapDispatchToProps = dispatch => ({
helloWorld: () => dispatch(call()),
logIn: (user2) => dispatch(logIn(user2)),
logOut: () => dispatch(logOut()),
})
export default connect(mapStateToProps, mapDispatchToProps)(Layout)
the page file login.js
import React from "react";
import { Typography, Button, FormControlLabel, Checkbox, TextField, Grid, Paper} from '#mui/material';
import { styled } from '#mui/material/styles';
import theme from '../theme';
import classes from '../styles/login';
import logo from '../img/Ergopak-logo.png';
import { Navigate } from 'react-router-dom';
// SXprops
class Login extends React.Component {
state = {
email: "",
password: "",
showPassword: false,
navigateTo: ""
}
componentDidMount(){
const {logOut} = this.props
logOut()
}
goToHome(){
}
handleSubmit(e){
const {logIn} = this.props
console.log("handleSubmit pressed!!")
logIn({name: "Pieka"})
this.setState({navigateTo: <Navigate to="/home"/>})
}
setEmail(){
console.log("handleEmail pressed")
}
setPassword(){
console.log("setPassword pressed")
}
render(){
const {user, requestState} = this.props
const {email, password, showPassword, navigateTo} = this.state
console.log("requestState", requestState)
console.log("user", user)
console.log("that thing is, ", classes(theme).welcome)
return(
<div style={{backgroundColor: "green", height: "100%", position: "fixed", width: "100%"}}>
<Grid container direction = "column" alignItems="stretch" justifyContent="flex-start" style={{backgroundColor: "grey", height: "100%", display: "flex"}} >
{navigateTo}
<Grid item sx = {classes(theme).welcomeDiv}>
<div sx = {classes(theme).welcome} >Welcome To SightPlan </div>
</Grid>
<Grid item >
<img style={{maxHeight: "2em"}} src={logo} alt="logo" />
</Grid>
<Grid item sx = {classes(theme).centerDivs}>
<Typography>Please log in with your email address and password</Typography>
<TextField
sx={{
width: 400
}}
type="email"
value={email}
placeholder="Email"
onChange={(e) => this.setState({email: e.target.value})}
/>
<Grid container direction = "column" alignItems = "center">
<TextField
sx = {classes(theme).loginFields}
type= {showPassword ? "string" : "password"}
variant="standard"
value={password}
placeholder="Password"
onChange={(e) => this.setState({password: e.target.value})}
/>
<FormControlLabel
control={<Checkbox size="small" name="showClosed" />}
checked={showPassword}
label={<span style={{ fontSize: '0.8em' }}> Show password </span>}
onChange={e => this.setState({showPassword: !showPassword})}
style={{margin: "auto"}}
/>
</Grid>
<Button
sx = {classes(theme).loginButton}
type="submit" onClick={this.handleSubmit.bind(this)}
variant="contained"
color="primary"
>
Log In
</Button>
</Grid>
<Paper style = {{backgroundColor: "blue", borderRadius: "0em", display: "flex", flexDirection: "column"}} />
</Grid>
</div>
)}
}
export default styled(Login)({theme})
The styles file login.js
import commonstyles from "./common"
const styles = (theme) => ({
centerDivs: commonstyles(theme).centerDivs,
welcomeDiv: commonstyles(theme).welcomeDiv,
white: commonstyles(theme).white,
logo: {
margin: "auto",
marginTop: "2em",
marginBottom: "2em",
...commonstyles(theme).logo,
},
welcome: {
fontSize: "3em",
fontFamily: "Segoe UI",
color: "white",
marginBottom: "1em",
margin: theme.spacing(2),
textAlign: "center"
},
loginFields: {
...commonstyles(theme).textField,
width: "20em"
},
loginButton: {
...commonstyles(theme).button,
width: "10em"
},
myTextBox: {
...commonstyles(theme).textField,
// "& .MuiInputBase-root": {
// color: 'black',
// borderColor: "green"
// }
}
})
export default styles
the theme file theme.js
import { createTheme } from '#mui/material/styles';
const theme = createTheme({
palette: {
primary: {
main: "#065f92", // blue
},
secondary: {
main: "#F79007", // orange
},
error: {
main: "#d32f2f",
},
warning: {
main: "#ed6c02",
},
info: {
main: "#0288d1",
},
success: {
main: "#2e7d32",
},
},
spacing: (factor) => `${factor}rem`,
});
export default theme;
and finally the App.js file that pulls it all together:
import React from "react";
import { Provider } from 'react-redux'
import theme from './theme';
import { ThemeProvider } from '#mui/material/styles';
import "./App.css";
import store from "./app/store";
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Login from "./containers/Login";
function App() {
return (
<ThemeProvider theme={theme}>
<Provider store={store}>
<div className="App" style={{maxHeight: "80%"}}>
<Router>
<Routes>
<Route path="/" element={<Login />} />
<Route path="/login" element={<Login />} /></Routes>
</Router>
<div style={{clear:"both", "height": "40px"}}></div>
</div>
</Provider>
</ThemeProvider>
);
}
export default App;

Related

Passing Data From One Component to Another React

Hey guys and gals i am trying to pass data from a text field in one component to another component. But the component that calls the text field is called in my Home.tsx file while i need the data from my Search.tsx file ( which also has where the text field is created ) to be sent to my searchResults.tsx file. Hope that makes sense
Here is my Home.tsx
import { ChakraProvider, Flex, Heading, IconButton, Input, Spacer, useColorMode, useDisclosure, VStack } from "#chakra-ui/react";
import Search from "../components/Search";
const Home = () => {
return(
<div>
<Flex>
<Search></Search>
</Flex>
</div>
)
}
export default Home
Here is my Search.tsx
import { TextField, IconButton} from "#material-ui/core"
import {Router, SearchOutlined } from '#material-ui/icons';
import { makeStyles } from "#material-ui/core/styles";
import "../App.css";
import { useState } from "react";
import { ChakraProvider,Box, color, position } from "#chakra-ui/react";
import { ReactComponent as Logo } from '../images/logo.svg';
const useStyles = makeStyles({
input: {
color: "blue"
}
})
const Search = () => {
const [searchTerm, setSearchTerm] = useState("");
const [displaySearchTerm, setDisplaySearchTerm] = useState("");
const useStyles = makeStyles((theme) => ({
input: {
color: "#fcba03",
},
}));
const classes = useStyles();
return(
<div>
<div className='searchCont' style={{alignItems: "center", justifyContent: 'center'}} >
<Logo style={{ height: 150, width: 300, position: "absolute", verticalAlign: "middle", marginTop: 200, left: '40%', backgroundColor: '#1a202c', borderRadius: 10 }} />
<TextField
inputProps={{color: classes.input}}
style={{width: 600, display: "flex", position: "absolute", verticalAlign: "middle", backgroundColor: "white", borderRadius: 5, alignItems: "center", justifyContent: 'center', marginTop: 400, marginBottom: 50, left: '32%', marginRight: 200}}
margin="normal"
className="textField"
fullWidth
id="standard-bare"
variant="outlined"
label="Search for Domains..."
onChange={(e) => setSearchTerm(e.target.value)}
InputProps={{
endAdornment: (
<IconButton onClick={() =>{searchTerm}}>
<SearchOutlined />
</IconButton>
),
}}
/>
</div>
</div>
)
}
export default Search
Here is my searchResults.tsx
import { Flex } from "#chakra-ui/react";
import React from "react";
import {searchTerm} from "../components/Search";
const SearchResults = () => {
return(
<div>
<Flex>
<Search data={searchTerm} /> //error here
</Flex>
</div>
)
}
export default SearchResults;
use Redux as your state management system for this, this will help you read and write states across application without worrying.
You can also try react Context if its a one off issue and you dont wana invest into the redux system and its boiler plate code, but if you face this issue more frequently as your project grows complex use redux.

Why are my testing-library queries not working?

I am using Jest/React Testing Library(RTL) to to unit testing for the UI.
None of my RTL queries work.
It keeps telling me errors like
TestingLibraryElementError: Unable to find an element by: [data-testid="analysis-categories-header"]
no matter how I query it.
I have tried getBy*, findBy*(yes, with async...await), getByTestId and everything.
The only difference is that when I use findBy* I get an extra error that says
Error: Error: connect ECONNREFUSED 127.0.0.1:80
even screen.debug() is ignored and not working.
Out of the 13 tests I have, only one test work surprisingly. The queries go through.
test:
import '#testing-library/jest-dom';
import { render, screen } from '#testing-library/react';
import { HelmetProvider } from 'react-helmet-async';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import Login from '../src/pages/login';
test('renders logo and login box with "LOG IN" text and two buttons', () => {
const initState = {};
const mockStore = configureStore();
render(
<HelmetProvider>
<Provider store={mockStore(initState)}>
<Login />
</Provider>
</HelmetProvider>,
);
expect(screen.getByTestId('login-logo')).toBeVisible();
expect(screen.getByTestId('login-title')).toBeVisible();
expect(screen.getByRole('button', { name: 'Sign In' })).toBeVisible();
expect(
screen.getByRole('button', { name: 'Sign In With Intuit' }),
).toBeVisible();
});
component:
import { useAuth0 } from '#auth0/auth0-react';
import {
Box,
Button,
Card,
CardContent,
Container,
Typography,
} from '#material-ui/core';
import Link from 'next/link';
import { useRouter } from 'next/router';
import type { FC } from 'react';
import React, { useEffect } from 'react';
import { Helmet } from 'react-helmet-async';
import Logo from 'src/components/Logo';
import axios from 'src/lib/axios';
import gtm from 'src/lib/gtm';
const Login: FC = () => {
const router = useRouter();
const { isLoading, isAuthenticated, loginWithRedirect } = useAuth0();
useEffect(() => {
gtm.push({ event: 'page_view' });
}, []);
useEffect(() => {
if (!isLoading && isAuthenticated) {
router.push('/recommendations');
}
}, [isLoading, isAuthenticated]);
const handleIntuitLogin = async () => {
try {
const response = await axios.get('/auth/sign-in-with-intuit');
window.location = response.data;
} catch (e) {
throw new Error(e);
}
};
const handleAuth0Login = async () => {
try {
const response = await axios.get('/auth/sign-in-with-auth0');
window.location = response.data;
} catch (e) {
throw new Error(e);
}
};
return (
<>
<Helmet>
<title>Login</title>
</Helmet>
<Box
sx={{
backgroundColor: 'background.default',
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
}}
>
<Container maxWidth="sm" sx={{ py: '80px' }}>
<Box
sx={{
display: 'flex',
justifyContent: 'center',
mb: 8,
}}
>
<Link href="/login">
<Box>
<Logo height={100} width={300} />
</Box>
</Link>
</Box>
<Card>
<CardContent
sx={{
display: 'flex',
flexDirection: 'column',
p: 4,
}}
>
<Box
sx={{
alignItems: 'center',
display: 'flex',
justifyContent: 'space-between',
mb: 3,
}}
>
<div>
<Typography
color="textPrimary"
gutterBottom
variant="h4"
data-testid="login-title"
>
Log in
</Typography>
</div>
</Box>
<Box
sx={{
flexGrow: 1,
mt: 3,
}}
>
<Button
color="primary"
onClick={handleAuth0Login}
fullWidth
size="large"
type="button"
variant="contained"
>
Sign In
</Button>
</Box>
<Box sx={{ mt: 2 }}>
<Button
color="primary"
onClick={handleIntuitLogin}
fullWidth
size="large"
type="button"
variant="contained"
>
Sign In With Intuit
</Button>
</Box>
</CardContent>
</Card>
</Container>
</Box>
</>
);
};
export default Login;
The only difference is that this component (nextjs page) does not use redux thunk. This is how far I got to find the cause.

material-ui backdrop -- fails with invalid hook call

I have been trying to use material ui backdrop element in my code. I am referring to this example: https://mui.com/components/backdrop/#example
As soon as I introduce this code into my component, it fails with error message as below.
Unhandled Runtime Error Error: Invalid hook call. Hooks can only be
called inside of the body of a function component. This could happen
for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug
and fix this problem.
My code is as below.
import React, { useState } from 'react'
import { makeStyles } from '#material-ui/core/styles'
import { Box,Typography } from '#material-ui/core'
import CategoryCard from '../shop-categories/category-card'
import { useSelector } from 'react-redux';
//import NavigateBeforeIcon from '#material-ui/icons/NavigateBefore';
//import ProductsInCategory from '../../../pages/shops/product-in-category';
import {useRouter} from 'next/router'
import Backdrop from '#mui/material/Backdrop'
import CircularProgress from '#mui/material/CircularProgress';
import { withStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
heading : {
fontFamily: 'Roboto',
fontSize: 14,
fontStyle: 'normal',
fontWeight: 700,
lineHeight: 1,
color: props => props.pColor
}
})
export default function CategoryGrid(props) {
const { categories, shopId, title} = props;
const shopColors = useSelector(state => state.main.currentShop)
const classes = useStyles(shopColors);
const router = useRouter()
const [open, setOpen] = React.useState(false);
const handleClose = () => {
setOpen(false);
};
const gotoSubCategories = (shopId, id,cat_Name) => {
setOpen(true)
router.push(`/shops/shop-sub-categories?shop=${shopId}&category=${id}&categoryName=${cat_Name}`)
}
return (
<>
<div>
<Backdrop sx={{color: '#fff', zIndex: (theme) => theme.zIndex.drawer +1}}
open={open}
onClick={handleClose}
>
<CircularProgress color='inherit' />
</Backdrop>
</div>
<Typography variant="body2" style={{ fontWeight: 'bold' }}
className={classes.heading}>
{title}
</Typography>
<Box display="flex" mb={2} mx={1} flexDirection="column">
<Box display="flex" fontWeight='fontWeightBold' width={1/2} mb={1}>
</Box>
<Box display="flex" flexWrap="wrap">
{
categories.map((category, index) => {
return <CategoryCard key={index}
name={category.name}
onClick={() => gotoSubCategories(shopId, category.id,category.name)}
imageURL={category.url}
shopId={shopId} callParent={props.callParent}
/>
})
}
</Box>
</Box>
</>
)
}
If I remove out this portion, then it works fine.
<div>
<Backdrop sx={{color: '#fff', zIndex: (theme) => theme.zIndex.drawer +1}}
open={open}
onClick={handleClose}
>
<CircularProgress color='inherit' />
</Backdrop>
</div>
How to fix this?

React, how do I persist the state of side drawer selected list item with router? [duplicate]

This question already has an answer here:
How do you get Material-UI Drawer to highlight the page it is currently at?
(1 answer)
Closed 1 year ago.
I'm using react-router and material UI. I integrated the routes with my material UI persist drawer list items with some custom styling like when I click on a list item it highlights it. But I'm facing an issue when I refresh the page my selected list item gets reset, even though I'm still on the same page. Can anyone tell me how do I persist the selected list item even if a page gets refreshed?
Inshort: Issue- when I refresh the page my drawer list item selected color get reset to top item even though I'm on the same page.
Here is the gif to demonstrate my issue
Here is my sandbox code link
Below is the code for the same
Note: I would suggest you go through my code sandbox it'll be better for you to lookup in code. Thank you
App.js
import { Switch, Route } from "react-router-dom";
import Login from "./pages/Login";
import Dashboard from "./pages/Dashboard";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Switch>
<Route path="/dashboard" component={Dashboard} />
<Route path="/" exact component={Login} />
</Switch>
</div>
);
}
Dashboard.jxs for nested routes in dashboard
import { Switch, Redirect, Route, useRouteMatch } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import NavBar from "../components/NavBar";
const Dashboard = () => {
const { path } = useRouteMatch();
return (
<>
<NavBar>
<Switch>
<Route path={`${path}/about`} component={About} />
<Route path={`${path}/home`} component={Home} />
<Redirect to={`${path}/home`} />
</Switch>
</NavBar>
</>
);
};
export default Dashboard;
NavBar.js
import React from "react";
import clsx from "clsx";
import { makeStyles, useTheme } from "#material-ui/core";
import CssBaseline from "#material-ui/core/CssBaseline";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import { useRouteMatch } from "react-router-dom";
import SideDrawer from "./SideDrawer";
const drawerWidth = 240;
const useStyles = makeStyles((theme) => ({
root: {
display: "flex"
},
appBar: {
transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
},
appBarShift: {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: drawerWidth,
transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
})
},
menuButton: {
marginRight: theme.spacing(2)
},
hide: {
display: "none"
},
drawer: {
width: drawerWidth,
flexShrink: 0
},
drawerPaper: {
width: drawerWidth
},
drawerHeader: {
display: "flex",
alignItems: "center",
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar,
justifyContent: "flex-end"
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
marginLeft: -drawerWidth
},
contentShift: {
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
}),
marginLeft: 0
}
}));
export default function NavBar({ children }) {
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = React.useState(true);
const { url } = useRouteMatch();
const handleDrawerOpen = () => {
if (!open) {
setOpen(true);
} else {
setOpen(false);
}
};
return (
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
className={clsx(classes.appBar, {
[classes.appBarShift]: open
})}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
className={clsx(classes.menuButton)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
Persistent drawer
</Typography>
</Toolbar>
</AppBar>
{/* child compoent to render side drawer and state is passing to open & close */}
<SideDrawer open={open} />
<main
className={clsx(classes.content, {
[classes.contentShift]: open
})}
>
<div className={classes.drawerHeader} />
{children}
</main>
</div>
);
}
SideDrawer.js its a child component inside NavBar.js
import {
useTheme,
Divider,
Drawer,
IconButton,
List,
ListItem,
ListItemIcon,
makeStyles
} from "#material-ui/core";
import React from "react";
import ChevronLeftIcon from "#material-ui/icons/ChevronLeft";
import ChevronRightIcon from "#material-ui/icons/ChevronRight";
import DrawerList from "./DrawerList";
const drawerWidth = 240;
const useStyles = makeStyles((theme) => ({
drawer: {
width: drawerWidth,
flexShrink: 0
},
drawerPaper: {
width: drawerWidth
},
drawerHeader: {
display: "flex",
alignItems: "center",
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar,
justifyContent: "flex-end"
}
}));
const SideDrawer = (props) => {
const theme = useTheme();
const classes = useStyles();
const { open } = props;
return (
<Drawer
className={classes.drawer}
variant="persistent"
anchor="left"
open={open}
classes={{
paper: classes.drawerPaper
}}
>
<div className={classes.drawerHeader}>
<h1>Header</h1>
</div>
<Divider />
<List>
{/* my component to render list of icons in side drawer */}
<DrawerList />
</List>
</Drawer>
);
};
export default SideDrawer;
DrawerList.js child component of SideDrawer.js Issue is arising here
import React, { useState } from "react";
import ListItem from "#material-ui/core/ListItem";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import { DashboardOutlined, AddCircleOutline } from "#material-ui/icons";
import ListItemText from "#material-ui/core/ListItemText";
import { makeStyles, Typography } from "#material-ui/core";
import Box from "#material-ui/core/Box";
import { Link, useRouteMatch } from "react-router-dom";
const useStyles = makeStyles((theme) => ({
root: {
marginTop: theme.spacing(1)
},
iconStyle: {
margin: theme.spacing(0, 0),
color: "#676767"
},
iconTitle: {
margin: theme.spacing(0, 0, 0, 1),
color: "#676767"
},
listItem: {
"&.Mui-selected": {
// it is used to change external svg color during click
"& path": {
fill: "#fff"
},
margin: theme.spacing(1.5, 1)
}
}
}));
const DrawerList = ({ children }) => {
console.log(children);
const [selectedIndex, setSelectedIndex] = useState(0);
const classes = useStyles();
const { url } = useRouteMatch();
const itemList = [
{
text: "Home",
icon: <DashboardOutlined />,
keys: "home",
to: `${url}/home`
},
{
text: "Appointment",
icon: <AddCircleOutline />,
keys: "about",
to: `${url}/about`
}
];
const ListData = () =>
itemList.map((item, index) => {
const { text, icon, to, keys } = item;
return (
<ListItem
className={classes.listItem}
button
key={keys}
to={to}
component={Link}
selected={index === selectedIndex}
onClick={(e) => handleListItemClick(e, index)}
style={
selectedIndex === index
? {
background: "#3f51b5",
width: 200,
marginLeft: 8,
paddingLeft: 10,
borderRadius: 4,
boxShadow: "2px 3px 6px rgba(0, 0, 0, 0.3)"
}
: {}
}
>
<ListItemIcon
className={classes.iconStyle}
style={selectedIndex === index ? { color: "#fff" } : {}}
>
{icon}
<ListItemText>
<Typography
component="div"
className={classes.iconTitle}
style={selectedIndex === index ? { color: "#fff" } : {}}
>
<Box fontWeight={700} fontSize={13.8}>
{text}
</Box>
</Typography>
</ListItemText>
</ListItemIcon>
</ListItem>
);
});
const handleListItemClick = (e, index) => {
setSelectedIndex(index);
};
return (
<div className={classes.root}>
<ListData />
</div>
);
};
export default DrawerList;
You need to track the url to show the selected items instead of a useState that gets reseted:
const { url } = useRouteMatch()
const {pathname} = useLocation();
const itemList = [
{
text: "Home",
icon: <DashboardOutlined />,
keys: "home",
to: `${url}/home`
},
{
text: "About",
icon: <AddCircleOutline />,
keys: "about",
to: `${url}/about`
}
];
...
selected={pathname === to}
To persist data between page refresh, you can use localStorage API.
You need to initialize your state with the value from localStorage. And whenever you update your react state, you also need to update the value in localStorage, so that after page is refreshed, your component state gets initialized with this stored value.
For eg:
const storedOpen = JSON.parse(localStorage.getItem('drawerOpen'));
const [open, setOpen] = React.useState(storedOpen);
const handleDrawerOpen = () => {
if (!open) {
setOpen(true);
localStorage.setItem('drawerOpen', true);
} else {
setOpen(false);
localStorage.setItem('drawerOpen', false);
}
};

React - How do a I set the Dropdown Menu state onChange()?

I am trying to bind the onChange event of the dropdown menu to set the value to the selection. At the moment I can pass a reference to call handleChange. However since I don't know how to bind the dropdownmenu object to the this. I can't access this.state
Maybe my code structure need to shift to something like the demo: http://www.material-ui.com/#/components/dropdown-menu
However, if i do that, how would I pass in documentList?
So confused.
Gratefully appreciate a little support.
import React from 'react';
import { ListGroup, Alert, Row, Col} from 'react-bootstrap';
import Paper from 'material-ui/Paper';
import TextField from 'material-ui/TextField';
import FlatButton from 'material-ui/FlatButton';
import RaisedButton from 'material-ui/RaisedButton';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import DropDownMenu from 'material-ui/DropDownMenu'
const handleChange = (event, index, value) =>
{
console.log("handle change (value) ", value);
console.log("handle change (event) ", event);
console.log("handle change (index) ", index);
//How do i set the state of the dropdown object?
}
export const widget = ({ documentList }) => (
documentList.length > 0 ? <Paper style={{ paddingTop: 16,
paddingBottom: 16,
marginTop: 3,
}}>
<form style={{ padding: 30 }} className="add-update-form" onSubmit={() => false}>
<Row>
<Col md={2}>
<DropDownMenu value={2} onChange={handleChange} openImmediately={true}>
<MenuItem value={1} primaryText="Starter" />
<MenuItem value={2} primaryText="Mains" />
<MenuItem value={3} primaryText="Drinks" />
</DropDownMenu>
</Col>
</Row>
The component that you wrote is a stateless functional component, which is supposed to work just like a normal function. There is no state object in this case. You will need a class constructor in order to have access to the state object in your component.
import React from 'react';
import { ListGroup, Alert, Row, Col} from 'react-bootstrap';
import Paper from 'material-ui/Paper';
import TextField from 'material-ui/TextField';
import FlatButton from 'material-ui/FlatButton';
import RaisedButton from 'material-ui/RaisedButton';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import DropDownMenu from 'material-ui/DropDownMenu';
class Widget extends React.Component {
constructor(props) {
super(props);
}
handleChange(event, index, value) {
console.log("handle change (value) ", value);
console.log("handle change (event) ", event);
console.log("handle change (index) ", index);
//Set States
}
render() {
const documentList = this.props.documentList;
//ES6 object destructing
// const { documentList } = this.props;
return() {
if(!documentList.length > 0) return;
<div>
<Paper style={{ paddingTop: 16,
paddingBottom: 16,
marginTop: 3,
}}>
<form style={{ padding: 30 }} className="add-update-form" onSubmit={() => false}>
<Row>
<Col md={2}>
<DropDownMenu value={2} onChange={handleChange} openImmediately={true}>
<MenuItem value={1} primaryText="Starter" />
<MenuItem value={2} primaryText="Mains" />
<MenuItem value={3} primaryText="Drinks" />
</DropDownMenu>
</Col>
</Row>
</div>
}
}
}
export default Widget;
This is the current version of Widget.js
import React from 'react';
import { ListGroup, Alert, Row, Col} from 'react-bootstrap';
import Paper from 'material-ui/Paper';
import TextField from 'material-ui/TextField';
import FlatButton from 'material-ui/FlatButton';
import RaisedButton from 'material-ui/RaisedButton';
import Snackbar from 'material-ui/Snackbar';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import DropDownMenu from 'material-ui/DropDownMenu'
import Toggle from 'material-ui/Toggle';
class Widget extends React.Component {
constructor(props) {
super(props);
}
handleChange(event, index, value) {
console.log("handle change (value) ", value);
console.log("handle change (event) ", event);
console.log("handle change (index) ", index);
//Set States
}
styles = {
block: {
maxWidth: 250,
},
toggle: {
marginBottom: 16,
},
thumbOff: {
backgroundColor: '#ffcccc',
},
trackOff: {
backgroundColor: '#ff9d9d',
},
thumbSwitched: {
backgroundColor: 'red',
},
trackSwitched: {
backgroundColor: '#ff9d9d',
},
labelStyle: {
color: 'red',
},
customWidth: {
width: 200,
},
};
render() {
const documentList = this.props.documentList;
return () => {
documentList.length > 0 ?
<Paper style={{
paddingTop: 16,
paddingBottom: 16,
marginTop: 3,
}}>
<form style={{padding: 30}} className="add-update-form" onSubmit={() => false}>
<Row>
<Col md={2}>
<DropDownMenu value={2} onChange={this.handleChange} openImmediately={true}>
<MenuItem value={1} primaryText="Starter" />
<MenuItem value={2} primaryText="Mains" />
<MenuItem value={3} primaryText="Drinks" />
</DropDownMenu>
</Col>
<Col md={2}>
<Toggle
label="Dessert"
labelPosition="right"
style={this.styles.toggle}
/>
</Col>
</Row>
<RaisedButton label="Save" primary={true} style={{marginRight: 15, marginTop: 15}} onClick=""/>
</form>
</Paper> :
<Alert bsStyle="warning">No stuff yet.</Alert>
}
}
}
Widget.propTypes = {
documentList: React.PropTypes.array,
};
export default Widget;
Haven't got a clue why, but managed to get the pages to render. So though i would share the solution..... I think it's soomething to do with prp types and maybe the version of reat i'm using... "react": "^15.6.1", might upgrade later when i get a moment and see what happens. However the page renders so I am happy. Just need to fix the event handling.
import React from 'react';
import { ListGroup, Alert, Row, Col} from 'react-bootstrap';
import Paper from 'material-ui/Paper';
import TextField from 'material-ui/TextField';
import FlatButton from 'material-ui/FlatButton';
import RaisedButton from 'material-ui/RaisedButton';
import Snackbar from 'material-ui/Snackbar';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import DropDownMenu from 'material-ui/DropDownMenu'
import Toggle from 'material-ui/Toggle';
export class OrderWidget extends React.Component {
constructor(props) {
super(props);
this.state = {
toggle: true,
},
this.styles = {
block: {
maxWidth: 250,
}
,
toggle: {
marginBottom: 16,
}
,
thumbOff: {
backgroundColor: '#ffcccc',
}
,
trackOff: {
backgroundColor: '#ff9d9d',
}
,
thumbSwitched: {
backgroundColor: 'red',
}
,
trackSwitched: {
backgroundColor: '#ff9d9d',
}
,
labelStyle: {
color: 'red',
}
,
customWidth: {
width: 200,
}
,
}
}
handleChange(event, index, value) {
console.log("handle change (value) ", value);
console.log("handle change (event) ", event);
console.log("handle change (index) ", index);
//Set States
this.state.setValue(value);
}
render() {
return (
<Paper style={{
paddingTop: 16,
paddingBottom: 16,
marginTop: 3,
}}>
<form style={{padding: 30}} className="add-update-form" onSubmit={() => false}>
<Row>
<Col md={2}>
<DropDownMenu value={2} onChange={this.handleChange} openImmediately={true}>
<MenuItem value={1} primaryText="Starter" />
<MenuItem value={2} primaryText="Mains" />
<MenuItem value={3} primaryText="Drinks" />
</DropDownMenu>
</Col>
<Col md={8}>
<TextField floatingLabelText="Units" hintText="Units" name="Units" ref="Units"
key="Units" defaultValue="" fullWidth={false}/>
<TextField floatingLabelText="Price" hintText="Price" name="Price" ref="Price"
key="Price" defaultValue="" fullWidth={false}/>
</Col>
<Col md={2}>
<Toggle
label="Side"
labelPosition="right"
style={this.styles.toggle}
/>
</Col>
</Row>
<RaisedButton label="Save" primary={true} style={{marginRight: 15, marginTop: 15}}
onClick=""/>
</form>
</Paper>
);
}
}
/*
Widget.propTypes = {
documentList: React.PropTypes.array,
};
*/

Categories

Resources