Material UI is breaking in production with NEXT js - material-ui

Someone help me, I built an application with NEXT and Material UI and now for some reason even though the dev server works perfectly fine the productio build is breaking. I tried to alter my _document.ts as shown below:
import Document, { Html, Head, Main, NextScript } from 'next/document';
import React from 'react';
import { ServerStyleSheets } from '#mui/styles';
export default class MyDocument extends Document {
render() {
return (
<Html lang='en'>
<Head>
<link rel='preconnect' href='https://fonts.googleapis.com' />
<link rel='preconnect' href='https://fonts.gstatic.com' />
<link
href='https://fonts.googleapis.com/css2?family=Roboto:wght#100;300;400&display=swap'
rel='stylesheet'
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
// Render app and page and get the context of the page with collected side effects.
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};
and this is _app.tsx:
import '../styles/root.scss';
import '../styles/global/_page.scss';
import { Provider } from 'react-redux';
import type { AppProps } from 'next/app';
import Notification from '../components/Common/Notifications/Notification';
import store from '../redux/app/store';
import React from 'react';
import MIUIHeader from '../components/Common/MUIComponents/MUI-Header/MUIHead';
export default function MyApp({ Component, pageProps }: AppProps) {
React.useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
//#ts-ignore
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
return (
<Provider store={store}>
<React.Fragment>
<MIUIHeader />
<Component {...pageProps} />
<Notification />
</React.Fragment>
</Provider>
);
}
And here's my Material UI Component:
import React, { ReactElement } from 'react';
import TextField from '#mui/material/TextField';
import LocalPhoneIcon from '#mui/icons-material/LocalPhone';
import MuiPhoneNumber from 'material-ui-phone-number';
import Autocomplete from '#mui/material/Autocomplete';
import Button from '#mui/material/Button';
import MailIcon from '#mui/icons-material/Mail';
import PersonIcon from '#mui/icons-material/Person';
import LocationOnIcon from '#mui/icons-material/LocationOn';
import LocationCityIcon from '#mui/icons-material/LocationCity';
import BusinessCenterIcon from '#mui/icons-material/BusinessCenter';
import ShareIcon from '#mui/icons-material/Share';
import classes from './VibeForms.module.scss';
import CorporateFareIcon from '#mui/icons-material/CorporateFare';
import ImageCarasoul from '../../../ImageCarasoul/ImageCarasoul';
interface Props {}
const imagePaths = [
'/screen-graphics/signup/individual/graphic-1.svg',
'/screen-graphics/signup/individual/graphic-2.svg',
'/screen-graphics/signup/individual/graphic-3.svg',
];
const maxMultilineRows = 3;
export default function LoginForm({}: Props): ReactElement {
return (
<React.Fragment>
<div className={classes.LoginColumnGrid}>
<ImageCarasoul imagePaths={imagePaths} />
<div className={classes.LoginColumn}>
<div className={classes.LoginRowGrid}>
<PersonIcon />
<TextField
required
className={classes.FormInputField}
label='Full Name'
size='small'
type='text'
/>
</div>
<div className={classes.LoginRowGrid}>
<MailIcon />
<TextField
required
className={classes.FormInputField}
label='Email Address'
size='small'
type='email'
/>
</div>
<div className={classes.LoginRowGrid}>
<LocalPhoneIcon />
<MuiPhoneNumber
variant='outlined'
name='phone'
data-cy='user-phone'
required
className={classes.FormInputField}
label='Phone Number'
size='small'
defaultCountry={'in'}
onChange={(e) => console.log(e)}
/>
</div>
<div className={classes.LoginRowGrid}>
<ShareIcon />
<TextField
required
className={classes.FormInputField}
label='Social Media Links'
size='small'
type='text'
multiline
maxRows={maxMultilineRows}
/>
</div>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<Autocomplete
disablePortal
options={[
'Workshop/Training',
'Social Media Promotion',
'Our Tie Ups',
'Networking',
]}
renderInput={(params) => (
<TextField
{...params}
label='Services you are looking for'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<TextField
required
className={classes.FormInputField}
label='Any Other Specific Requests?'
size='small'
type='text'
multiline
maxRows={maxMultilineRows}
/>
</div>
</div>
<div className={classes.LoginColumn}>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<Autocomplete
disablePortal
options={['Beginer', 'Intermediate', 'Expert']}
renderInput={(params) => (
<TextField
{...params}
label='Enter your Profession'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<LocationOnIcon />
<Autocomplete
disablePortal
options={['Bangalore', 'Chennai', 'Hyderabad', 'Mumbai']}
renderInput={(params) => (
<TextField
{...params}
label='State'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<LocationCityIcon />
<Autocomplete
disablePortal
options={['Bangalore', 'Chennai', 'Hyderabad']}
renderInput={(params) => (
<TextField
{...params}
label='City'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<Autocomplete
disablePortal
options={['Beginer', 'Intermediate', 'Expert']}
renderInput={(params) => (
<TextField
{...params}
label='Profession Level'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<BusinessCenterIcon />
<TextField
required
className={classes.FormInputField}
label='Work Experience'
size='small'
type={'number'}
InputProps={{
endAdornment: <label>Years</label>,
}}
/>
</div>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<TextField
required
className={classes.FormInputField}
label='Your Achievements'
size='small'
type='text'
multiline
maxRows={maxMultilineRows}
/>
</div>
</div>
</div>
<Button
variant='contained'
style={{ alignSelf: 'end', margin: '1rem', width: '10rem' }}>
Next
</Button>
</React.Fragment>
);
}
I am using sass modules along with Material UI in NEXT. I tried all the solutions out there mentioned in github and stack overflow but nothing seems to work ;(.

Since most of the documents were outdated to an older version of Material UI, I found the solution in their github repo. They have updated it 20 days back and it works fine. In case someone may want to reference this in future, kindly keep checking this repo.
Here you will find the exact configuration you need to do for _app.tsx and _document.tsx. Additionally you need to also create two files for themes and createEmotionCache as well. The location doesn't matter though.

Related

material-ui autocomplete wrapped into react-hook-form Controller, can't get the value

I can't get the value of the field wrapped into the react-hook-form <Controller /> in the onSubmit function. What is missing?
the other field works correctly
const onSubmit = async (data,e) => {
console.log("DATA" , data)
//data.groups is always undefined
...
}
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="groups"
control={control}
as={() => (
<Autocomplete multiple options={fbGroupsData.Group} getOptionLabel={option => option.name} renderInput={(params) =>(<TextField {...params} variant="outlined" label="Groups" placeholder="Groups" />)}/>
)}
/>
<TextField fullWidth inputRef={register} InputLabelProps={{ shrink: true }} name="name" label="Name" variant="outlined" />
</form>
There is an example in the doc: https://codesandbox.io/s/react-hook-form-v6-controller-qsd8r which was recommended to use render prop instead of as:
<Controller
render={(props) => (
<Autocomplete
{...props}
options={countries}
getOptionLabel={(option) => option.label}
renderOption={(option) => (
<span>
{countryToFlag(option.code)}
{option.label}
</span>
)}
renderInput={(params) => (
<TextField
{...params}
label="Choose a country"
variant="outlined"
/>
)}
onChange={(_, data) => props.onChange(data)}
/>
)}
name="country"
control={control}
/>

TypeError: editorState.getCurrentContent is not a function

I am trying to console the result from react draft wysiwyg and I am getting editorState.getCurrentContent is not a function. I am not sure where did I go wrong. Many thanks in advance and greatly appreciate any helps. Thanks
import React,{useState} from 'react'
import { render } from 'react-dom';
import { Editor } from 'react-draft-wysiwyg';
import {EditorState} from "draft-js";
import { stateToHTML } from "draft-js-export-html";
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
const AddBlog= () => {
const [editorState, setEditorState] = useState(
() => EditorState.createEmpty(),
);
const handleChange = (editorState) =>{
const contentState = stateToHTML(editorState.getCurrentContent())
// JSON.stringify(convertToRaw(editorState.getCurrentContent()))
console.log(contentState)
}
return (
<div className="container-fluid">
<div className="card-wrapper-tutorial">
<div className="card">
<div className="card-body">
<h4 className="card-title">New Code Snippet</h4>
<form autoComplete="off">
<div className="form-group">
<label htmlFor="title">Title</label>
<input id="title" type="text" className="form-control" placeholder="title" name="title" disabled = {disabled} onChange={handleChange} />
</div>
<div className="form-group">
<label htmlFor="body">Body</label>
<Editor
defaultEditorState={editorState}
onEditorStateChange={setEditorState}
wrapperClassName="wrapper-class"
editorClassName="editor-class"
toolbarClassName="toolbar-class"
onChange={handleChange}
/>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary btn-block">
{loading && <i className="fa fa-refresh fa-spin"></i>}
Submit</button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}
export default AddBlog
You have the handleChange in two places which they don't belong
on the input's onChange, the callback for onChange would not
be an editorState but and input event e.
on the Editor
component, the callback for onChange would not be an editorState
also but a RawDraftContentState which is the string html.
You probably meant to place it in the onEditorStateChange for Editor:
<Editor
defaultEditorState={editorState}
onEditorStateChange={editorState => {
setEditorState(editorState);
handleChange(editorState);
}}
wrapperClassName="wrapper-class"
editorClassName="editor-class"
toolbarClassName="toolbar-class"
/>
or leaving the code as is and modifying the handleChange to expect RawDraftContentState
const handleChange = (rawDraftContentState) =>{
// no need for convertToRaw or stateToHtml anymore
console.log(rawDraftContentState)
}

How to show a label together with startAdornment in Multiple Select in Material UI?

I am using the Select with the multiple prop. The component works fine, and the InputLabel renders the renders the following:
However, when I add an icon at the beginning via the startAdornment prop, the InputLabel is moved above the input field.
Do you know how to show the label next do the icon when no values are selected?
The code is:
<FormControl className={classes.formControl} fullWidth variant="outlined" size="medium">
<InputLabel name={name} color="secondary" variant="outlined" htmlFor={id} id={id} className={classes.label}>{label}</InputLabel>
<Select
name={name}
labelId={id}
id={id}
multiple
autoWidth
startAdornment={icon}
color="secondary"
value={selectedValues}
onChange={handleChange}
renderValue={selectedOptions => (
<div className={classes.tags}>
{selectedOptions.map(option => (
<Chip key={option.value} label={option.label} size="small" className={classes.chipStyle} />
))}
</div>
)}
>
{options.map(option => (
<MenuItem key={option.value} value={option}>
<Checkbox checked={logicHere} color="primary" />
<ListItemText primary={option.label} />
</MenuItem>
))}
</Select>
</FormControl>
Did you try ?
<Select
InputProps={{
startAdornment: <InputAdornment position="start">Kg</InputAdornment>,
}} />

Getting a "FB.login() called before FB.init()." when I run this code locally,

I can't seem to be able to login to my app that I'm making when I click on my "Log in with Facebook" button. Also, I'm using React JS. I think it might have something to do with not having REACT_APP_FACEBOOKLOGIN=######### (where ######### is my Facebook Developer ID) inside of a .env file.
import React, { Component } from 'react';
import logo from './logo.png';
import FacebookLogin from 'react-facebook-login';
import Sidebar from "react-sidebar";
import LeaveGroup from "./components/LeaveGroup";
// import firebase from "./utils/firebase.js";
import { GoogleApiWrapper } from 'google-maps-react';
import MapBox from "./components/MapBox";
// import ReactDOM from 'react-dom'
import ChatBox from "./components/ChatBox";
import API from "./utils/API";
import './App.css';
import CreateGroup from './components/CreateGroup/CreateGroup';
require("dotenv").config();
class App extends Component {
constructor(props) {
super(props);
this.state = {
sidebarOpen: true,
name: "",
id: "",
chatText: "",
messagesArray: [],
groupName: "",
};
this.onSetSidebarOpen = this.onSetSidebarOpen.bind(this);
}
onSetSidebarOpen(open) {
this.setState({ sidebarOpen: open });
};
componentDidMount() {
this.loadUsers();
}
loadUsers = () => {
API.getUsers()
.then(res =>
this.setState({ users: res.data }, () => console.log("loadUsers Data: ", res.data))
)
.catch(err => console.log(err));
};
saveUsers = (data) => {
API.saveUser(data)
.then(res =>
console.log(res))
}
handleOnClick = event => {
event.preventDefault();
if (this.state.name) {
API.saveUser({
name: this.state.name,
})
.then(res => this.loadUsers())
.catch(err => console.log(err));
}
};
responseFacebook = (response) => {
console.log(response);
this.setState({ name: response.name, id: response.id })
this.saveUsers(response);
}
render() {
return (
<div className="App">
<Sidebar
sidebar={<b>
<button onClick={() => this.onSetSidebarOpen(false)}>
×
</button>
<div>
<img id="logo-image" src={logo} alt="catchup-app-logo" />
</div>
<div className="about-text">
{/* <p>The CatchUp! app allows you to
create, share and join private location based groups.
</p> */}
</div>
<div
style={{ padding: 40 }}>
<br />
<FacebookLogin
appId={process.env.REACT_APP_FACEBOOKLOGIN}
autoLoad={true}
reauthenticate={true}
fields="name,email,picture"
callback={this.responseFacebook}
/>
<br />
<br />
</div>
{/* Create group box */}
<CreateGroup
groupName={this.state.groupName}
groupMember={this.state.name}
/>
{/* Join group box */}
<div className="text-box">
<p>
<button class="btn btn-dark" type="button" data-toggle="collapse" data-target="#collapseExample1" aria-expanded="false" aria-controls="collapseExample">
Join a Group
</button>
</p>
<div class="collapse" id="collapseExample1">
<div class="card card-body">
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="Group Name" aria-label="Group Name" aria-describedby="button-addon2" />
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" id="button-addon">Join</button>
</div>
</div>
</div>
</div>
</div>
<ChatBox
name={this.state.name}
groupName={this.state.groupName}
messagesArray={this.state.messagesArray}
/>
</b>}
open={this.state.sidebarOpen}
onSetOpen={this.onSetSidebarOpen}
styles={{ sidebar: { background: "white" } }}
>
<button onClick={() => this.onSetSidebarOpen(true)}>
<i class="fas fa-bars"></i>
</button>
</Sidebar>
<br />
<br />
<MapBox
gProps={this.props.google}
gZoom={17}
gOnMarkerClick={this.gOnMarkerClick}
gName={this.state.name}
gGroupName={this.state.groupName}
gOnClose={this.onInfoWindowClose}
/>
</div>
);
}
}
export default GoogleApiWrapper({
apiKey: `${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}`
})(App)
I want this to be able to get this to work so move onto logging in a user into my MongoDB.

Form Input value property not behaving as expected

I am trying to create a form to collect input to create an event listing.
I am using the 'value' property in way it is documented in the React docs:
https://reactjs.org/docs/forms.html
,to collect user input onChange, but material-ui:
https://material-ui.com/api/input/
uses 'value' for setting value to the input field not collecting the value in the field. This is causing all sort of bugs, e.i. not being able to enter input, pickers not displaying default values and not accepting values and also not being able to collect information. I could use pointers on where I am going wrong. Thanks for having a look :-)
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Input from '#material-ui/core/Input';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
const styles = theme => ({
container: {
display: 'flex',
flexWrap: 'wrap',
}
});
class CreateEvent extends React.Component{
constructor() {
super();
this.state = {
title: "",
description: "",
location: "",
start_time: "",
start_date: "",
end_time: "",
end_date: ""
};
}
updateInput = e => {
this.setState({
[e.target.name]: e.target.value
});
};
cancelCreation = e =>{
};
addNewEvent = e =>{
//Make db call
};
render(){
return(
<div className="form-container">
<Paper className="new-event-form">
<form className="event-form" onSubmit={this.addNewEvent}>
<Input
type="text"
placeholder="Event title"
className="event-title"
inputProps={{
'aria-label': 'Description',
}}
multiline={1}
rows={1}
onChange={this.updateInput}
value={this.state.value}
/>
<Input
type="text"
placeholder="Event description"
className="event-title"
inputProps={{
'aria-label': 'Description',
}}
multiline={true}
rows={10}
onChange={this.updateInput}
//value={this.state.description}
/*
Will allow user to provide input because 'value' is commented out. But the above component will not because value is referenced
*/
/>
<Grid container spacing={16} className="event-grid">
<Grid item xs={4}>
<Input
type="text"
item xs={4}
placeholder="Event location"
className="event-location"
inputProps={{
'aria-label': 'Description',
}}
multiline={true}
rows={4}
onChange={this.updateInput}
//value={this.state.location}
/>
</Grid>
<Grid item xs={4}>
<TextField
id="date"
label="Start date"
type="date"
defaultValue="2017-05-24"
className="event-start-date"
InputLabelProps={{
shrink: true,
}}
onChange={this.updateInput}
value={this.state.start_date}
/>
<TextField
id="time"
label="Start time"
type="time"
defaultValue="07:30"
className="event-start-time"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 min
}}
/>
</Grid>
<Grid item xs={4}>
<TextField
id="date"
label="End date"
type="date"
defaultValue="2017-05-24"
className="event-start-date"
InputLabelProps={{
shrink: true,
}}
/>
<TextField
id="time"
label="End time"
type="time"
defaultValue="07:30"
className="event-start-time"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 min
}}
/>
</Grid>
</Grid>
<Button className="line-spacer"/>
<Grid container className="form-buttons">
<Grid item xs={12}>
<Input type="file" name="my-event-image" id="file" className="new-event-image"> </Input>
<label htmlFor="file">CHOOSE AN IMAGE</label>
<Button className="line-spacer" onChange={this.updateInput}/>
</Grid>
</Grid>
<Grid container spacing={16} className="form-buttons">
<Grid item xs={6}>
<Button onChange={this.cancelCreation}>Cancel</Button>
</Grid>
<Grid item xs={6}>
<Button type="submit">Submit</Button>
</Grid>
</Grid>
</form>
</Paper>
</div>
);
}
}
export default withStyles(styles)(CreateEvent);
In your updateInput method you use e.target.name but none of your input fields have a name property. Add a name property on each Input component matching the name you are using in your value. For example:
<Input name="description" ... value={this.state.description} .../>