this is undefined in Babel-compiled methods - reactjs

I'm trying to write a small webapp with React 0.13 and ES6 syntax. I'm using webpack and babel-loader to compile:
loaders: [
{ test: /\.js/, exclude: /node_modules/, loader: "babel-loader"}
]
I'm having trouble with the this variable inside methods, getting "this is undefined" in several places throughout my code. For example:
export class PanelEditor extends React.Component {
...
update (){
if (!this.isMounted())
return;
this.setState(this.getStateFromStore());
}
...
}
The this variable should never be undefined under this conditions. However, I found that the problem might be the way Babel rewrites the code:
update: {
value: function update() {
if (!this.isMounted()) {
return;
}
this.setState(this.getStateFromStore());
}
},
In that way, it seems to me, the this variable refers to the object literal instead of the class. How can I fix this?

That's not actually the problem. It's that this is undefined, because you're not binding the update function.
You can do this in the constructor or in render. Most people do it in render.
<div onClick={this.update.bind(this)} />
Or (my preference), an arrow function which preserves this.
<div onClick={() => this.update()} />

Related

How to load css provided by webpack inside an IFrame created using "react-frame-component" library?

I'm trying to render my React component inside an iframe. I got it to work by using the library "react-frame-component". The problem is that the styles are loaded outside the iframe, at the end of "head" element. I want to change it to be loaded inside the "head" of "iframe" element.
I'm using Webpack to generate the bundles with JS and CSS included and I saw that I can change where "style-loader" will load the styles by setting the option "insertInto", but this is throwing an error:
Uncaught Error: Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.
This is my React component:
import Frame from 'react-frame-component'
...
ReactDOM.render(
<Frame id="someId">
<Provider store={Store.get()}>
<Container/>
</Provider>,
</Frame>,
document.body
);
This is my "style-loader" in Webpack configuration:
{
loader: 'css-loader',
options: {
insertInto: () => document.querySelector("#someId"),
},
}
I think the problem is that the component was not rendered when webpack tried to include the styles. How to solve this?
You would need to load the styles into a temporary element as placeholder, and then use Javascript to clone those styles into the new frame.
// index.html
...
<div id="dynamic-styles"></div>
...
// webpack.config.js
...
{
loader: 'css-loader',
options: {
insertInto: () => document.getElementById("dynamic-styles"),
},
}
...
// some.js
...
const stylesContainer = document.getElementById('dynamic-styles');
const frame = document.getElementById('someID');
const frameHead = frame.contentWindow.document.head;
[...stylesContainer.children].forEach(styleNode => {
const newStyle = document.createElement('style');
newStyle.textContent = styleNode.textContent;
frameHead.appendChild(newStyle);
});
I haven't tested the above snippet but I know the concept works. I feel it gets the point across.
There may be a better way to do this but I haven't looked into it enough to figure it out yet. It'd be nice to be able to get webpack to make separate style bundles so that each configured frame could just automatically lazy load it's own dedicated bundled css

bind(this) in ReactComponent.render() method

I have a quite big React.Component with more than ten bind(this) calls in its constructor.
I don't think .bind(this) in a constructor gives any help to understand my code especially when reading the code inside of render().
So, I came up with the following idea, however, do not find how to achieve this.
render() {
if (this.methodToBind.getThis() === this) {
this.methodToBind = this.methodToBind.bind(this);
}
}
Is there any possible way I can get this of a method (getThis() from the example above)?
If yes for the above, is it a good practice to do this?
rather then doing this,
constructor () {
super();
this.myFunc = this.myFunc.bind(this);
}
myFunc () {
// code here
}
You can do something like this.
constructor () {
super();
// leave the binding, it's just not what the cool kids do these days
}
myFunc = () => {
// see what I did here with = () => {}
// this will bind your `this` with it's parent
// lexical context, which is your class itself, cool right. Do this!
}
For a reference have a look at the MDN documentation for Arrow Functions
Where to Bind This?
When you create a function in a Class Based Component in React.js you must bind this in order to call it from the render method. Otherwise the function will not be in scope.
There are a few ways to do this.
Never bind this in the render method. Binding this in the render method will cause React to create a new function every time your Component is rerendered. This is why we most commonly bind in the constructor.
Bind in the constructor. By binding in the constructor, you can call your function in the render method by using this.FunctionName();
Example Bind This
Class MyClass extends Component {
constructor(props) {
super(props);
this.FunctionName = this.FunctionName.bind(this);
}
myFunc () {
// code here
}
render() {
this.FunctionName();
return(
<div>
...
</div>
);
}
}
User fat arrow functions instead of traditional function definitions. Fat arrow functions lexically bind the this value. So you do not have to add bind to the function in the constructor when you use the fat arrow function in the class.
Important - Fat arrow functions are not always available to use in a React class. Depending on how you setup React. You might have to install,
babel-plugin-transform-class-properties
Then add it to your .bablerc file like this,
{
"plugins": [
"transform-class-properties"
],
"presets": [
"react"
]
}
Example Fat Arrow
Class MyClass extends Component {
myFunc = () => {
// code here
}
render() {
this.FunctionName();
return(
<div>
...
</div>
);
}
}
Summary
Never bind a function in the render.
Always bind in the constructor when using a traditional function
this is automatically available on the function when using a fat arrow function.
Not sure.
I am usually doing something like this:
onClick={this.handleClick.bind(this)}
or:
onClick={e => this.handleClick(e)}

React setState callback not called for external (npm packaged) component

I am using setState with a callback to ensure things run after the state has been updated.
...
this.setState({enabled : true},this.getData);
}
getData () {
const self = this;
fetch(this.props.url,{
method : 'post',
body : this.state
}).then(function (response) {
return response.json();
}).then(function (result) {
self.update();
});
}
...
setState is getting called. this.state.enabled does change to true. However, this.getData is not getting called.
One thing I found interesting is that this is happening for a component I am using via an npm package. In my own code, setState with a callback works as designed.
The said external component is also packaged by me. I am using webpack to build it. Might there be something wrong with my webpack config?
Here it is:
const Webpack = require('webpack');
const path = require('path');
module.exports = {
entry: {
index : './src/index.js'
},
output: {
path: path.join(__dirname,'dist'),
filename: '[name].js',
library : 'TextField',
libraryTarget: 'umd'
},
externals : [
{
'react' : {
root : 'React',
commonjs2 : 'react',
commonjs : 'react',
amd : 'react'
}
}
],
module: {
loaders: [
{ test: /\.(js?)$/, exclude: /node_modules/, loader: require.resolve('babel-loader'), query: {cacheDirectory: true, presets: ['es2015', 'react', 'stage-2']} }
]
},
devtool : 'eval'
};
Edit:
So now I am pretty sure something fishy is going on when I use my component from a package vs, when I use it from my source.
When I call setState from a component which is part of my source-code, this is what is called:
ReactComponent.prototype.setState = function (partialState, callback) {
!(typeof partialState === 'object'
|| typeof partialState === 'function'
|| partialState == null) ?
process.env.NODE_ENV !== 'production' ?
invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.')
: _prodInvariant('85')
: void 0;
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
The above is from a file named ReactBaseClasses.js
When I call setState from a component I packaged as an npm package, this is what is called:
Component.prototype.setState = function (partialState, callback) {
!(typeof partialState === 'object'
|| typeof partialState === 'function'
|| partialState == null) ?
invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.')
: void 0;
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
The above is from a file named react.development.js. Notice that callback is passed to enqueueSetState. Interestingly, when I break into enqueueSetState, the function does nothing with the callback:
enqueueSetState: function (publicInstance, partialState) {
if (process.env.NODE_ENV !== 'production') {
ReactInstrumentation.debugTool.onSetState();
process.env.NODE_ENV !== 'production' ? warning(partialState != null, 'setState(...): You passed an undefined or null state object; ' + 'instead, use forceUpdate().') : void 0;
}
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
if (!internalInstance) {
return;
}
var queue = internalInstance._pendingStateQueue ||
(internalInstance._pendingStateQueue = []);
queue.push(partialState);
enqueueUpdate(internalInstance);
},
This could be a bug with React. Apparently it was a major bug fix to combine the enqueueSetState and enqueueCallback functionality, but these were separately functioning calls in earlier versions of React.
https://github.com/facebook/react/issues/8577
When importing npm modules, sometimes they bring in different versions of React as dependencies, creating these kinds of inconsistency bugs with setState and callbacks.
https://github.com/facebook/react/issues/10320
When you set your state, do it like this.
this.setState({enabled : true}, () => { this.getData() });
The () => {} bind this to it's lexical parent context.
You could (and maybe actually should) call the getData method from componentDidUpdate. It will also happen 100% after the state has been changed and (IMHO) will make your code a little less callback hellish.
Update following comments:
You can compare your previous state/props with the current ones in componentDidUpdate and then call your method if needed:
componentDidUpdate(prevProps, prevState) {
if (prevProps.something !== this.props.something) {
// do some magic
}
}
You mentioned the callback is being called successfully from handleLookupChange, correct?
So handleChange is successfully being called from handleLookupChange after the onEnter event is triggered and is successfully setting the value for state, correct? And that value for state is either
{displayText : self.props.dataSource[selectedIndex]}
or
{displayText : newValue}.
So here is one possibility.
I noticed handleLookupChange takes two values as arguments from onEnter rather than an event. It sounds like you said the onEnter event successfully delivers the two arguments to handleLookupChange and correctly sets state based on them. If not my guess would be handleLookupChange ought to take the event and process it to pass to handleChange.
Or another one.
If that transition is happening successfully, could this be an issue in how values are received from input after the onEnter event?
When you trigger handleLookupChange with onEnter, is that possible that hitting enter with no text in the input somehow returns newValue not as null but as the empty string? This would cause displayText to be set as newValue (as it is not null), thus causing displayText to be set as the empty string, causing the callback addItem's if statement never to trigger and run?
Have you checked that addItem for sure isn't being called above the if statement?
Another thing to check would be whether dataSource at selectedIndex might be picking up the empty string, which could trigger the if statement to not run the inside of addItem.

web pack css-loader generated css class names with react-bootstrap

I'm using css-loader for web pack, and the configuration looks like this:
loaders: [
{
test: /\.css$/,
loader: ExtractTextPlugin.extract('style-loader', 'css-loader?camelCase&modules')
}, ...]
And then in my jsx file I have something like this:
import styles from 'components/MyComponent/style.css'
export default class MyComponent extends React.Component {
render() {
return (
return <div className={styles.myComponent}>
<Media>
<Media.Left>
...
</Media.Left>
<Media.Body>
...
</Media.Body>
</Media>
</div>
)
}
}
And in my components/MyComponent/style.css file I have something like:
.myComponent .media-left {
vertical-align: middle;
}
And so my problem is, css-loader will generate random ids for both .myComponent and .media-left, which is seriously annoying. Because .media-left is a bootstrap class and I want it just left alone. Is there a way to make css-loader only generate an id for the top level css class?
You can have one loader for bootstrap and the other for the rest of your css, based on test config.
On another note, the modules part of your css-loader config is responsible random class names. You can use localIdentName config to format generated classnames to your liking.
So I figured this out. I need to use the global selector for every class that I want to remain global, something like this:
.myComponent :global(.media-left) {
vertical-align: middle;
}

React animation / ReactCSSTransitionGroup

How would you make the following link - a spinner - work with reactjs
http://codepen.io/awesome/pen/znGwr
I am using webpack:
{ test: /\.css$/, loader: "style-loader!css-loader" }
I know the css is working from other settings.
I have tried the following, using the same css file(copy paste into local css ).
Updated: I do "return" the element, but did not write it in here. Felix Kling, thanks for noting this.
ES6:
render(){
return
<ReactCSSTransitionGroup transitionName="spinner" >
<div key='test' className="spinner"></div>
</ReactCSSTransitionGroup>
}
or
render(){
return
<div key='test' className="spinner"></div>
}
This(both) just gives me a circle and no animation.
Since you asked in your comments for an answer...
I believe the problem is that you were copying over the uncompiled SCSS instead of the compiled CSS.
Hope this helps! :D

Resources