I have included material-ui (and react-tap-event-plugin) in my project and added 3 buttons to one of my components:
<RaisedButton onClick={this.props.onSave} label="Save" style={styles.button}/>
<RaisedButton label='Publish' onClick={this.props.onPublish} style={styles.button}/>
<RaisedButton label='Cancel' onClick={this.onCancel.bind(this)} style={styles.buttonCancel}/>
when I click on any of these, they go very dark grey and when I click again, they go black (and stay like that). The whole applications goes bonkers, the react routing no longer works (I can see the URL changing in the address bar, but the view doesn't refresh). This all looks pretty bad for a button click :)
Any idea what I may be doing wrong? (I take care of the childContext as described in the docs, so the muiTheme is loaded).
I forgot to check the console... there are 3 exceptions whenever I press the button:
1)
vendor.js:12 Uncaught Error: addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's render method, or you have multiple copies of React loaded (details: https://facebook.github.io/react/warnings/refs-must-have-owner.html).(…)
2)
ReactTransitionGroup.js:176 Uncaught TypeError: Cannot read property 'componentWillLeave' of undefined(…)
3)
vendor.js:12 Uncaught Error: removeComponentAsRefFrom(...): Only a ReactOwner can have refs. You might be removing a ref to a component that was not created inside a component's render method, or you have multiple copies of React loaded (details: https://facebook.github.io/react/warnings/refs-must-have-owner.html).(…)
In the component that uses FlatButton (or RaisedButton neither work) I have this:
1) Import:
import FlatButton from 'material-ui/FlatButton'; //eslint-disable-line
import baseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
2) in the class
getChildContext() {
return { muiTheme: getMuiTheme(baseTheme) };
}
3) and a static declaration:
EditorComponent.childContextTypes = {
muiTheme: React.PropTypes.object.isRequired,
};
Feels like I'm doing all that's required.
This may be related to what I'm experiencing:
https://github.com/callemall/material-ui/issues/2818
So probably the issue is caused by material-ui distributing it's own version of React? What's the point in that? But... my version of material-ui doesn't have a node_modules folder, so no extra React either...
Source for a component importing and using FlatButton
import React from 'react'; // eslint-disable-line
import Input from '../../../components/common/textInput'; // eslint-disable-line
import BaseEditorComponent from '../base/EditorComponent';
import FlatButton from 'material-ui/FlatButton'; //eslint-disable-line
import baseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
export default class EditorComponent extends BaseEditorComponent {
constructor() {
super();
this.state = {
textValue: 'Enter value'
};
}
getChildContext() {
return { muiTheme: getMuiTheme(baseTheme) };
}
_onChange(e) {
this.setState({
textValue: e.target.value
});
}
render() {
return (
<div>
<Input
value={this.state.textValue}
name="SimpleText"
label="Simple Text Value:"
onChange={this._onChange.bind(this)}
/>
<FlatButton label="Test"/>
</div>
);
}
}
EditorComponent.childContextTypes = {
muiTheme: React.PropTypes.object.isRequired,
};
Also, the BaseEditorComponent:
import React from 'react';
import widgetActions from '../../widgets/WidgetActions';
import widgetInstanceStore from '../../widgets/WidgetInstanceStore';
export default class EditorComponent extends React.Component {
constructor() {
super();
}
componentDidMount() {
this.setState(widgetInstanceStore.getWidgetInstanceState(this.props.widgetId) || {});
}
save() {
widgetActions.saveWidgetInstanceState(this.props.widgetId, this.state);
}
}
Have you tried to use onTouchTap instead of onClick?
If #1 doesn't help, please show more code - component with above code and it's parent component.
As per https://github.com/callemall/material-ui/issues/2818 the solution was to include react-addons-transition-group alongside react in the browserify bundle. So it's good to know that it's not only NPM where a 2nd copy of react can slip through, but also browserify or webpack.
Thanks https://stackoverflow.com/users/3706986/piotr-sołtysiak for helping with the issue today!
Related
I've started to code my first React app and it's awesome, but I can't figure out how to manage css files per-component(so the actual CSS won't load if it is not necessary).
React with webpack(correct me if I'm wrong please) wraps the project in such a way that at every given moment the app loads only what it needs(in terms of JS).
So if I have my main App component with only two buttons visible: btn-01 and btn-02, and inside of this component I have another two: component-01 and component-02, and they are hidden till the corresponded button is clicked(btn-01 for component-01), these components won't be loaded until the actual button is clicked(am I getting this right?), however this is not the same with css as I can tell, because I see the css of each of these(component-01 and component-02) components loaded right away the App is loaded, even though none of the buttons are clicked.
I'm not a big fan of inline styling, but I did test it with css module, but the result is the same in this aspect. So I'm not even sure if this is possible to implement in an easy way.
Here's a code, so perhaps I'm not implementing it correctly, but please don't mind the none-DRY code etc.
So as you may see, the style of Component-01 and -02 are loaded even though there is no need for them at the moment(none of the button is pressed).
App.js
import React, { Component } from "react";
import "./App.css";
import Component_01 from "./Component-01/Component-01";
import Component_02 from "./Component-02/Component-02";
class App extends Component {
state = {
isComponent_01: false,
isComponent_02: false,
};
toggleComponent01 = () => {
this.setState({
isComponent_01: !this.state.isComponent_01,
});
};
toggleComponent02 = () => {
this.setState({
isComponent_02: !this.state.isComponent_02,
});
};
render() {
let showComponent_01 = null;
if (this.state.isComponent_01) {
showComponent_01 = <Component_01 />;
}
let showComponent_02 = null;
if (this.state.isComponent_02) {
showComponent_02 = <Component_02 />;
}
return (
<div className="App">
<button className="btn-01" onClick={this.toggleComponent01}>
Btn-01
</button>
<button className="btn-02" onClick={this.toggleComponent02}>
Btn-02
</button>
{showComponent_01}
{showComponent_02}
</div>
);
}
}
export default App;
Component-01.js (and Component-02.js, just with -02.js)
import React from "react";
import style from "./Component-01.module.css";
function App() {
return <div className={style["component-01"]}>Component-01</div>;
}
export default App;
Below is a screen shot of my package size from Next JS. What I want to point out is the react-color components under node_modules. I am importing them this way:
import { GithubPickerProps, GithubPicker, AlphaPicker } from 'react-color';
But you see it includes all the things I'm not using such as photoshop.js, sketch.js, etc.
How do I get it not to bundle the things I'm not using with tree shaking?
I did notice that import { debounce } from 'lodash'; imported all of lodash but import debounce from 'lodash/debounce'; reduced the package size by 200kB.
In order for the tree-shaking to work properly react-color should add module property to the package.json which will point to esm version of the lib.
Since it doesn't have it, you will need import directly.
Before:
import React from 'react'
import SketchPicker from 'react-color'
class Component extends React.Component {
render() {
return <SketchPicker />
}
}
After:
import React from 'react'
import SketchPicker from 'react-color/lib/Sketch'
class Component extends React.Component {
render() {
return <SketchPicker />
}
}
I'm using the package react-translate to localise my app.
import React from 'react';
import { translate } from 'react-translate';
class Hello extends React.Component {
render() {
return (
<div>
{this.props.t('test_string')}
</div>
);
}
}
export default translate('Hello')(Hello);
In the snippet above, translate is a High Order Component, that adds the function t to the properties of Hello.
Everything works fine but lint keeps complaining because t is not in the propTypes.
error 't' is missing in props validation react/prop-types
Is that normal? I guess I'm doing something wrong but I cannot tell what...
Edit:
As #stevejay says, I could add the t to my propTypes but I don't like this solution because - from my total ignorance in react - 1) t is not a property of the component itself, nor something I want to manually pass and 2) I have to add the property to all the models where I have already added the HOC and it seems redundant
To silence the linter, you need to just add propTypes to your Hello component:
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-translate';
class Hello extends React.Component {
...
}
Hello.propTypes = {
t: PropTypes.func.isRequired
}
Any props that a component you create uses should be declared in that way.
Using Mobx, after updating the store (i.e. clicking the button) the component does not re-render. I've installed mobx devtools which shows nothing after the initial load, and there is no error in the console. Any ideas what I've done wrong?
Store.js:
import { observable } from 'mobx';
class Store {
#observable me;
constructor() {
this.me = 'hello';
}
change_me(){
this.me = 'test 1234';
}
}
export default Store;
layout.js:
import React from "react";
import { observer } from 'mobx-react';
#observer
export default class Layout extends React.Component{
render(){
return(
<div>
<h1>{this.props.store.me}</h1>
<button onClick={this.on_change}>Change</button>
</div>
)
}
on_change = () => {
this.props.store.change_me();
}
}
index.js:
import React from "react";
import ReactDOM from "react-dom";
import Layout from "./components/Layout";
import Store from "./Store";
import DevTools, { configureDevtool } from 'mobx-react-devtools';
// Any configurations are optional
configureDevtool({
// Turn on logging changes button programmatically:
logEnabled: true,
// Turn off displaying conponents' updates button programmatically:
updatesEnabled: false,
// Log only changes of type `reaction`
// (only affects top-level messages in console, not inside groups)
logFilter: change => change.type === 'reaction',
});
const app = document.getElementById('app');
const store = new Store();
ReactDOM.render(
<div>
<Layout store={store} />
<DevTools />
</div>
, app);
I would start by adding #action to your change_me() function. From what I understand, it's not always completely required, but I have encountered problems like this in my own code several times when I've forgotten to add it.
Additionally post your .babelrc as #mweststrate suggested, as it will help others to check that the proper plugins are loaded.
Just add makeObservable(this); in constructor function like below
constructor() {
makeObservable(this);
}
My guess would be to have uninitialized #observable. It is very counter-intuitive, but Babel doesn't handle those well. Even adding #observable me = undefined might help (see the generated js code when you assign something there. Generally I'd remove constructor completely and move the initialization to declaration (i.e. #observable me = "hello" an no constructor). It should then work fine.
Watch the binding of the this context.
<button onClick={this.on_change}>Change</button>
the this reference will not be to the class, so likely when you are actually clicking it is going to say something along the lines of no props on undefined. Changing to:
<button onClick={this.on_change.bind(this)}>Change</button>
should fix it. Or better yet, bind the context in the constructor so its not re-binding on every render
constructor(props) {
super(props)
this.on_change = this.on_change.bind(this)
}
then you can go back to your
I am working on a JS app using React 0.14 and Babel 5.8.23.
My app works fine in Chrome, with no warnings, but when I view the app in IE9 the app explodes showing:
SCRIPT5022: Exception thrown and not caught
on the line
ReactDOM.render(
When I trap the exception, it shows that it is being thrown from this code:
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
When I manually remove these throws from the generated index.js, the app proceeds normally, although I do see these warnings (possibly unrelated, and discussed at https://github.com/facebook/react/issues/4990):
Warning: MainLogo(...): React component classes must extend React.Component
All my components do extend React.Component:
import React, { Component } from 'react';
export default class MainLogo extends Component {
render() {
return (
<h1 className="logo">
<img src="/img/brand/logo.png" />
</h1>
);
}
};
Why would this _classCallCheck be being triggered in IE9, and what could I do differently to prevent it?
It turns out that the following are problems for IE9:
1.
import React, { Component } from 'react';
export default class Whatever extends Component { ...
I had to import React; and then ... extends React.Component.
2.
I had to export my connected components as non-top-level components, ie giving them a name within the file:
export class App extends React.Component {
...
}
export const AppContainer = connect(state => ({ routerState: state.router }), { pushState }) (App);
3.
I had to disable livereactload https://github.com/milankinen/livereactload, specifically removing it from .babelrc used by babel-plugin-react-transform.
Only completing ALL these steps allowed my app to run satisfactorily on IE9.