autocomplete is undefined with material-ui - reactjs

I have the following code in a jsx file and I get error:
Uncaught ReferenceError: AutoComplete is not defined
From what I see it should be working ok, Code:
import React, {Component} from 'react';
import { Autocomplete } from 'material-ui';
class MaterialUIAutocomplete extends Component {
constructor(props) {
super(props);
this.onUpdateInput = this.onUpdateInput.bind(this);
this.state = {
dataSource : [],
inputValue : ''
}
}
onUpdateInput(inputValue) {
}
render() {
return <AutoComplete
dataSource = {this.state.dataSource}
onUpdateInput = {this.onUpdateInput} />
}
}
export default MaterialUIAutocomplete;

It's a typo, you are importing Autocomplete and using AutoComplete.
Use these ways to import AutoComplete:
import { AutoComplete } from 'material-ui';
Or
import AutoComplete from 'material-ui/AutoComplete';
Update:
To render material-ui component we need to add the default theme and styling, include these lines in your component, like this:
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
const muiTheme = getMuiTheme({});
Then render the AutoComplete inside MuiThemeProvider:
render() {
return <MuiThemeProvider muiTheme={muiTheme}>
<AutoComplete
dataSource = {this.state.dataSource}
onUpdateInput = {this.onUpdateInput} />
</MuiThemeProvider>
}
Use this:
import React, {Component} from 'react';
import AutoComplete from 'material-ui/AutoComplete';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
const muiTheme = getMuiTheme({});
class MaterialUIAutocomplete extends Component {
constructor(props) {
super(props);
this.state = {
dataSource : [],
inputValue : ''
}
this.onUpdateInput = this.onUpdateInput.bind(this);
}
onUpdateInput(inputValue) {
}
render() {
return <MuiThemeProvider muiTheme={muiTheme}>
<AutoComplete
dataSource = {this.state.dataSource}
onUpdateInput = {this.onUpdateInput} />
</MuiThemeProvider>
}
}
export default MaterialUIAutocomplete;
Note: MuiThemeProvider is not required to include inside each component, you can use this in main page and then you can use any material-ui component inside any component.

this looks like a migration issue to me - you want to use the material-ui 0.x AutoComplete, but you have installed the new material-ui v1.x.
as such, you need to follow the Migration steps and in order to use any v0.x component, put this wherever you create/declare your themes:
<MuiThemeProvider theme={theme}>
<V0MuiThemeProvider muiTheme={themeV0}>
{/*Components*/}
</V0MuiThemeProvider>
Because the new 1.5 theme is available via props, the old one through context, you need to include both for AutoComplete to have reference to the old theme. I wouldn't do this unless you really need something from the old library, such as the AutoComplete widget.
https://material-ui.com/guides/migration-v0x/

Related

Injecting css to a window.document when using react portal

I'm using react portal to present a printable version of my react view. This is what I do:
import { Component } from "react";
import ReactDOM from "react-dom";
export default class PortalWindow extends Component {
container = document.createElement("div");
externalWindow = null;
componentDidMount() {
this.externalWindow = window.open();
this.externalWindow.document.body.appendChild(this.container);
}
componentWillUnmount() {
this.externalWindow.close();
}
render() {
return ReactDOM.createPortal(this.props.children, this.container);
}
}
import React from 'react';
import './order-print.css';
function OrderPrint(props) {
return (
<div className="order-print">
</div>
);
}
export default OrderPrint;
And there's a component called PurchaseOrder where I do the following:
class PurchaseOrder extends React.Component {
...//omitted for brevity
render(){
{this.state.shouldPrint && (
<PortalWindow>
<OrderPrint order={order} />
</PortalWindow>
)}
}
}
So with a click of a button, I change the shouldPrint state to true and the printable version is displayed in a new window. However, the import './order-print.css'; statement seems to have no effect, so the style is not applied to the div that's in my OrderPrint.jsx file.
Here's the content of the order-print.css file:
.order-print{
border:3 px solid red;
}
So apparently, react does not pass the css to the view that's loaded in a new window. Is there a way to make this possible.
I've actually tried using style instead of className and referencing a const object that has styling information, and in fact, it works. but it's just I prefer using classes to inline styling.

How to avoid repeating interface in Typescript React

It's my first app I try to build using Typescript. I want to keep styles and components in separate files to make the code more descriptive and clear. Project will consist of dozens of components and I'll use props to call the classes. Each component will look more or less like this:
import * as React from 'react'
import withStyles from "#material-ui/core/styles/withStyles"
import { LandingPageStyles } from "./landing-page-styles"
interface LandingPageProps {
classes: any
}
class LandingPage extends React.Component<LandingPageProps> {
get classes() {
return this.props.classes;
}
render() {
return(
<div className={this.classes.mainPage}>
Hello Typescript
</div>
)
}
}
export default withStyles(LandingPageStyles)(LandingPage)
And simplified styles module :
import { createStyles } from "#material-ui/core";
export const LandingPageStyles = () => createStyles({
mainPage: {
textAlign: "center",
minHeight: "100vh",
}
})
In every component I want to have the classes props with type of any. Is there a way to avoid declaring interface for each component? It works now but I don't like my current solution beacuse of repetition the same code in every single component.
The proper way to do it is as bellow. Material-ui expose WithStyles interface that you can inherit to include classes props. The main advantage is that you IDE will handle autocompletion for the defined jss class. But anyway Typescript is more verbose than Javacript. With React you often have to repeat obvious things.
import * as React from 'react'
import {withStyles, WithStyles} from "#material-ui/core"
import { LandingPageStyles } from "./landing-page-styles"
interface LandingPageProps extends WithStyles<typeof LandingPageStyles> {
}
class LandingPage extends React.Component<LandingPageProps> {
get classes() {
return this.props.classes;
}
render() {
return(
<div className={this.classes.mainPage}>
Hello Typescript
</div>
)
}
}
export default withStyles(LandingPageStyles)(LandingPage)
The best solution is to declare interface which extends WithStyles . So in component there is need to declare:
import * as React from 'react'
import withStyles, { WithStyles } from "#material-ui/core/styles/withStyles"
import { LandingPageStyles } from "./landing-page-styles"
interface LandingPageProps extends WithStyles<typeof LandingPageStyles>{
}
class LandingPage extends React.Component<LandingPageProps> {
get classes() {
return this.props.classes;
}
render() {
return(
<div className={this.classes.mainPage}>
Hello Typescript
</div>
)
}
}
export default withStyles(LandingPageStyles)(LandingPage)

Customize withStyles Material UI React styled components

I have a library of material-ui styled React components.
Styled components look like this:
import React from "react";
import withStyles from "#material-ui/core/styles/withStyles";
import headerStyle from "material-kit-pro-react/assets/jss/material-kit-pro-react/components/headerStyle.jsx";
class Header extends React.Component {
// ...
}
export default withStyles(headerStyle)(Header);
I want to customize the style of the component, but I want to keep the library untouched to avoid conflicts with future updates.
So I decided to create my own component:
import React from "react";
import withStyles from "#material-ui/core/styles/withStyles";
import Header from "material-kit-pro-react/components/Header/Header.jsx";
export default withStyles(theme => ({
primary: {
color: "#000"
}
}))(Header);
But with this approach the JSS engine creates two classes for the same component (Header-primary-25 WithStyles-Header--primary-12). That's not exactly a problem, but in my case there's a small conflict with a method in the component that expects only ONE class:
headerColorChange() {
const { classes, color, changeColorOnScroll } = this.props;
// ...
document.body
.getElementsByTagName("header")[0]
.classList.add(classes[color]) // classes[color] is a string with spaces (two classes)
;
// ...
}
Ok. It's not a big deal. I can modify that method and fix the problem.
But when I extend the class:
import React from "react";
import Header from "material-kit-pro-react/components/Header/Header.jsx";
export default class extends Header {
constructor(props) {
super(props);
this.headerColorChange = this.headerColorChange.bind(this);
}
headerColorChange() {
const { classes, color, changeColorOnScroll } = this.props
console.log(classes[color])
}
}
I get this error related to withStyles:
withStyles.js:125 Uncaught TypeError: Cannot read property '64a55d578f856d258dc345b094a2a2b3' of undefined
at ProxyComponent.WithStyles (withStyles.js:125)
at new _default (Header.jsx:11)
at new WithStyles(Header) (eval at ./node_modules/react-hot-loader/dist/react-hot-loader.development.js (http://localhost:8080/bundle.js:137520:54), <anonymous>:5:7)
And now I'm lost.
What's the best way to customize an already styled component?
How can I extend and override the constructor of a HoC (withStyles)?
Edit to provide a better and minimal example:
I have a component I can't modifiy because it's provided by a library:
./Component.jsx:
import React from "react";
import withStyles from "#material-ui/core/styles/withStyles";
class Component extends React.Component {
constructor(props) {
super(props);
this.headerColorChange = this.headerColorChange.bind(this);
}
componentDidMount() {
window.addEventListener("scroll", this.headerColorChange);
}
headerColorChange() {
const { classes } = this.props;
// THIS FAILS IF classes.primary IS NOT A VALID CLASS NAME
document.body.classList.add(classes.primary);
}
render() {
const { classes } = this.props;
return <div className={classes.primary}>Component</div>
}
}
export default withStyles(theme => ({
primary: {
color: "#000"
}
}))(Component);
I want to customize the primary color of the component. The straight way:
./CustomizedComponent.jsx:
import React from "react";
import withStyles from "#material-ui/core/styles/withStyles";
import Component from "./Component";
export default withStyles({
primary: {
color: "#00f"
}
})(Component)
Ok. The color of the has changed to #0ff. But then the component fails in document.body.classList.add(classes.primary) because classes.primary contains two class names concatenated (Component-primary-13 WithStyles-Component--primary-12). If I were allowed to modify the original component there would be no problem. But I can't, so I decided extend it and override the headerColorChange:
./CustomizedComponent.jsx:
import React from "react";
import withStyles from "#material-ui/core/styles/withStyles";
import Component from "./Component";
class MyComponent extends Component {
constructor(props) {
super(props)
this.headerColorChange = this.headerColorChange.bind(this);
}
headerColorChange() {
}
}
export default withStyles({
primary: {
color: "#00f"
}
})(MyComponent)
But then I get the error:
withStyles.js:125 Uncaught TypeError: Cannot read property '64a55d578f856d258dc345b094a2a2b3' of undefined
at ProxyComponent.WithStyles (withStyles.js:125)
at new MyComponent (Header.jsx:7)
at new WithStyles(Component) (eval at ./node_modules/react-hot-loader/dist/react-hot-loader.development.js (http://0.0.0.0:8080/bundle.js:133579:54), <anonymous>:5:7)
at constructClassInstance (react-dom.development.js:12484)
at updateClassComponent (react-dom.development.js:14255)
Questions are:
Is it possible to fully override the styles of the component so I get only one class name?
How can I extend a component returned by withStyles function as a higher order component?

React and setState and autocomplete

Im using react and material ui.
This is my component
```
import React from 'react';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import AutoComplete from 'material-ui/AutoComplete';
// the light theme
const lightMuiTheme = getMuiTheme(lightBaseTheme);
// the autocomplete component width
const Preferencestypeahead = {
maxWidth: 600,
position:'relative',
margin:'auto'
}
export default class Preferences extends React.Component {
constructor(props) {
super(props);
this.handleUpdateInput = this.handleUpdateInput.bind();
this.state = {
dataSource: [],
};
}
handleUpdateInput(value){
this.setState({
dataSource: [
value,
value + value,
value + value + value,
],
});
};
render() {
return (
<div>
<MuiThemeProvider muiTheme={lightMuiTheme}>
<section style={Preferencestypeahead}>
<AutoComplete
hintText="Type"
dataSource={this.state.dataSource}
onUpdateInput={this.handleUpdateInput.bind(this)}
floatingLabelText="Search"
fullWidth={true}
/>
</section>
</MuiThemeProvider>
</div>
)
}
}
I keep getting setState is not defined when I type anything inside the autocomplete. Where could I be going wrong? I have also faced this problem when I tried to import the tabs as well
You need to bind to "this"
This line is the problem:
this.handleUpdateInput = this.handleUpdateInput.bind();
Change it to:
this.handleUpdateInput = this.handleUpdateInput.bind(this);
Since you are already using ES6 you could just do
...
onUpdateInput={(val) => this.handleUpdateInput(val)}
...
then you don't need to do this --> this.handleUpdateInput = this.handleUpdateInput.bind(this); in your constructor

Changing material-ui theme on the fly --> Doesn't affect children

I'm working on a react/redux-application where I'm using material-ui.
I am setting the theme in my CoreLayout-component (my top layer component) using context (in accordance to the documentation). This works as expected on initial load.
I want to be able to switch themes during runtime. When I select a new theme, my redux store gets updated and therefore triggers my components to update. The problem is that the children of my CoreLayout-component doesn't get affected - the first time! If I repeatedly change my theme (using a select-list that sends out a redux-action onChange) the children are updated. If a child component is located 2 layers down in my project hierarchy, it is updated after 2 action calls - so there is some issue with how the context is passed down.
My CoreLayout.js component
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import ThemeManager from 'material-ui/lib/styles/theme-manager';
const mapStateToProps = (state) => ({
uiStatus: state.uiStatus
});
export class CoreLayout extends React.Component {
getChildContext() {
return {
muiTheme: ThemeManager.getMuiTheme(this.props.uiStatus.get("applicationTheme").toJS())
};
}
render () {
return (
<div className='page-container'>
{ this.props.children }
</div>
);
}
}
CoreLayout.propTypes = {
children: PropTypes.element
};
CoreLayout.childContextTypes = {
muiTheme: PropTypes.object
};
export default connect(mapStateToProps)(CoreLayout);
One of my child components (LeftNavigation.js)
import React from "react";
import { connect } from 'react-redux';
import { List, ListItem } from 'material-ui';
const mapStateToProps = (state) => ({
uiStatus: state.uiStatus
});
export class LeftNavigation extends React.Component {
render () {
return (
<div className="left-pane-navigation">
<List subheader="My Subheader" >
<ListItem primaryText="Search" />
<ListItem primaryText="Performance Load" />
</List>
</div>
);
}
}
LeftNavigation.contextTypes = {
muiTheme: React.PropTypes.object
};
export default connect(mapStateToProps)(LeftNavigation);
I can access the theme located in context by this.context.muiTheme.
I can get the component to update the theme by using another instance of getChildContext() inside each child component, but I will have such a large number of components that I would very much like to avoid having to do that.
My CoreLayout component's getChildContext-method is called when I change theme and all my child components gets re-rendered as expected.
Any ideas?
Update: It works as expected on mobile devices (at least iOS)
You can use muiThemeProvider to avoid having to add getChildContext to any child component, the provider does this for you.
...
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import MyAwesomeReactComponent from './MyAwesomeReactComponent';
const App = () => (
<MuiThemeProvider muiTheme={getMuiTheme()}>
<MyAwesomeReactComponent />
</MuiThemeProvider>
)
...
More info in documentation.

Resources