Using npm debounce I get an error with the following code in ReactJS. The error
Javascript - Uncaught TypeError: Object(...) is not a function
happens when the function is passed into debounce()
import React, { Component } from 'react';
import { debounce } from 'debounce';
class test extends Component {
constructor(props) {
super(props);
this.state = {
yolo: {}
};
}
foobar(param) {
debounce(() => {
this.setState({yolo: param});
}, 99);
}
render () {
return (
...
<SomeComponent action={this.foobar.bind(this)} />
...
);
}
}
I have tried some of the solutions mentioned in Perform debounce in React.js
but none seem to work.
import React, { Component } from 'react';
import debounce from 'debounce';
class test extends Component {
constructor(props) {
super(props);
this.state = {
yolo: {}
};
this.foobar.bind(this);
}
foobar(param) {
debounce(() => {
this.setState({yolo: param});
}, 99);
}
render () {
return (
...
<SomeComponent action={this.foobar.bind(this)} />
...
);
}
}
The top set of code should work. Ok, so the reason why your call to foobar was not working before was because you were missing this line: this.foobar.bind(this);. Your previous syntax worked just fine and is actually preferable to the this.foobar =. Reason being because one is ES6 syntax and the other is ES5. What that bind function does when you call it is attach a particular this context for when the function is called. Here is a reference to an article that explains that: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Now the second part of this was the import. when you use the object bracket syntax, thats actually called object destructuring. So what that does is whatever that object exports it tries to access a debounce property and make that accessible in the current file. Problem is I suspect that that npm package is already export a function as its default so you don't need to access something on it. Make sense?
Hope this all helps! Best of luck (thumbsup)
Related
Is it wrong to use setState in a function outside of the React component?
Example:
// myFunction.js
function myFunction() {
...
this.setState({ ... })
}
// App.js
import myFunction from './myFunction
class App extends Component {
constructor() {
super()
this.myFunction = myFunction.bind(this)
}
...
}
I'm not sure the way you're binding will actually work. You could do something like:
export const getName = (klass) => {
klass.setState({ name: 'Colin'})
}
then
class App extends Component {
state = {
name: 'React'
};
handleClick = () => {
getName(this);
}
render() {
return (
<div>
<p>{this.state.name}</p>
<button onClick={this.handleClick}>change name</button>
</div>
);
}
}
Working example here.
So the only reasons to do this is if you are reducing repeated code, e.g. two components use the same logic before calling this.setState, or if you want to make testing easier by having a separate pure function to test. For this reason I recommend not calling this.setState in your outside function, but rather returning the object you need so it can you can call this.setState on it.
function calculateSomeState(data) {
// ...
return { updated: data };
}
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = calculateSomeState(props.data);
}
handleChange = (e) => {
const value = e.target.value;
this.setState(calculateSomeState({ ...props.data, value }));
}
}
It looks like a bug waiting to happen... If you want to use an external function to set state, you can use the alternative syntax provided by React:
this.setState((prevState, props) => {
return updatedState; //can be a partial state, like in the regular setState
});
That callback can easily be extracted to an external function and it's guaranteed to work
It is not wrong, the function is never called outside the component. This is a mix-in technique. bind isn't needed, as long as the function isn't used as a callback. In this case myFunction is same among all instances, a more efficient way would be:
class App extends Component {}
App.prototype.myFunction = myFunction;
Suppose I had this layout below:
class Navigation extends React.Component {
primaryFun() { console.log('funn') }
secondaryFun() {
this.primaryFun();
}
}
I'd of expected this to then call primary but instead I get an undefined, Ok.
So I thought I'd add a constructor to bind the function to this:
constructor(props) {
super(props)
this.primaryFun = this.primaryFun.bind(this);
}
but primary fun is still undefined.
In my real project I'm calling these on a mouseOut event.
Feels like the above should work and tbh the documentation for React is all over the shot so couldn't find much here.
Are you looking for something like this calling one function inside the other
import React, { Component } from 'react';
import './App.css'
class App extends Component {
constructor(){
super()
this.mouseClick = this.mouseClick.bind(this);
this.primaryFun = this.primaryFun.bind(this);
this.secondaryFun = this.secondaryFun.bind(this);
}
primaryFun(){
console.log('primaryFun funn')
}
secondaryFun(){
console.log('secondaryFun funn')
this.primaryFun()
}
mouseClick(){
this.secondaryFun()
}
render() {
return (
<div onClick={this.mouseClick}>
Hello World!
</div>
);
}
}
export default App;
Here when you click on "Hello world" secondaryFun is called and inside secondaryFun , primaryFun is been triggered
You also need to bind the secondaryFun function to use this inside that. Without that, the this inside the function secondaryFun will refers to the function scope which is secondaryFun
You need to bind this in your mouseOut
onMouseOut={this.secondaryFun.bind(this)}
Or a as best practice use the Lambda syntax. It'll bind this for you
onMouseOut={()=>this.secondaryFun()}
Make sure both functions have the correct this scope. If you are using class properties, see https://babeljs.io/docs/plugins/transform-class-properties/. Which already present on the babel-preset-react-app used by create-react-app, you can use that and write those as arrow functions, as seen on the babel link. And avoid having to use .bind on the constructor.
You must bind() both two functions.
You should that:
class Navigation extends React.Component {
constructor(props) {
super(props)
this.primaryFun = this.primaryFun.bind(this);
this.secondaryFun = this.secondaryFun.bind(this);
}
primaryFun() {
console.log('funn')
}
secondaryFun() {
this.primaryFun();
}
}
In my app I use ExtJS with React. I've tried to override some functionalities but I faced some problems when defining custom Ext components. I suspect that this is caused by different scopes of "this".
There are 2 scenarios. The 1st one is working scenario, but achieved in non-elegant way. The 2nd scenario is desired but it doesn't work.
Scenario 1 - it works - codepen #1
index.js
// defined globally, it's not very nice
Ext.define('CustomContextMenu', {
extend: 'Dummy.plugin.ContextMenu',
createMenuItems: function() {
return this.callParent() // keep standard behaviour
}
});
ReactDOM.render(
React.createElement(CustomScheduleApp),
document.getElementById('schedule-app')
);
app.jsx
class CustomScheduleApp extends React.Component {
...
render() {
<CustomSchedule ...></CustomSchedule>
}
}
schedule.jsx
class CustomSchedule extends React.Component {
...
componentDidMount() {
let taskContextMenu = Ext.create("CustomContextMenu");
...
}
}
Scenario 2 - it doesn't work - codepen #2
index.js
ReactDOM.render(
React.createElement(CustomScheduleApp),
document.getElementById('schedule-app')
);
app.jsx
class CustomScheduleApp extends React.Component {
...
render() {
<CustomSchedule ...></CustomSchedule>
}
}
schedule.jsx
class CustomSchedule extends React.Component {
...
componentDidMount() {
Ext.define('CustomContextMenu', {
extend: 'Dummy.plugin.ContextMenu',
createMenuItems: function() {
return this.callParent() // keep standard behaviour
// "this" has fewer keys in this scenario, some data is missing
}
});
let taskContextMenu = Ext.create("CustomContextMenu");
...
}
}
Error message for Scenario #2:
Uncaught TypeError: Cannot read property 'apply' of null
I have a basic knowledge about scopes, I'm not even sure if it's more React of JavaScript issue. Am I missing something in my code?
Because the way React works and you are setting your extended method inside a React prototype, ExtJS callParent is getting confused about where the event is called from, so you would have to specify this manually.
addBodyCls: function(cls) {
// custom logic here
console.log("inside addBodyCls");
return this.superclass.addBodyCls.apply(this, arguments);
}
should do the trick!
I am trying to create a decorator method which will add some default lifecycle methods into the react component. My objective is to add some default functionality into the component, for example all component should be able to do a specific thing on componentWillMount.
I read a couple of articles and found this. It can be used to add new props to the react components.
export default function context(contextTypes, context) {
return function (DecoratedComponent) {
return class {
static childContextTypes = contextTypes;
getChildContext() {
return context;
}
render() {
return (
<DecoratedComponent {...this.props} />
);
}
}
}
}
But I am not sure how would I add class methods like componentWillMount. Can I do something like
Object.assign(DecoratedComponent.prototype, {
componentWillMount: () => {
// do something
}
})
Any idea towards right direction?
Refs:
http://asaf.github.io/blog/2015/06/23/extending-behavior-of-react-components-by-es6-decorators/
https://gist.github.com/motiz88/3db323f018975efce575
If you're using Babel with the stage 1 or stage 0 preset, you can use the following method:
First, define your decorator function, e.g.:
function lifecycleDefaults(target) {
target.prototype.componentWillMount = function() {
console.log('componentWillMount ran from decorator!');
console.log('this.props is still accessible', this.props);
}
target.prototype.componentWillUnmount = function() {
console.log('componentWillUnmount ran from decorator!');
console.log('this.props is still accessible', this.props);
}
target.prototype.componentDidMount = function() {
console.log('componentDidMount ran from decorator!');
console.log('this.props is still accessible', this.props);
}
}
Following that, decorate a component using the function you just defined e.g.:
#lifecycleDefaults
export class Page extends React.Component {
render() {
return (
<div>Hello decorators!</div>
);
}
};
Component 'Page' now has methods componentWillMount, componentDidMount, and componentWillUnmount. They run at the expected times in the component's lifecycle.
2 caveats: 1) I'm using the babel transform-decorators-legacy plugin; 2) I'm building my project using Webpack, with babel's transform-runtime included. YMMV.
I am following this tutorial: http://reactkungfu.com/2015/07/approaches-to-testing-react-components-an-overview/
Trying to learn how "shallow rendering" works.
I have a higher order component:
import React from 'react';
function withMUI(ComposedComponent) {
return class withMUI {
render() {
return <ComposedComponent {...this.props}/>;
}
};
}
and a component:
#withMUI
class PlayerProfile extends React.Component {
render() {
const { name, avatar } = this.props;
return (
<div className="player-profile">
<div className='profile-name'>{name}</div>
<div>
<Avatar src={avatar}/>
</div>
</div>
);
}
}
and a test:
describe('PlayerProfile component - testing with shallow rendering', () => {
beforeEach(function() {
let {TestUtils} = React.addons;
this.TestUtils = TestUtils;
this.renderer = TestUtils.createRenderer();
this.renderer.render(<PlayerProfile name='user'
avatar='avatar'/>);
});
it('renders an Avatar', function() {
let result = this.renderer.getRenderOutput();
console.log(result);
expect(result.type).to.equal(PlayerProfile);
});
});
The result variable holds this.renderer.getRenderOutput()
In the tutorial the result.type is tested like:
expect(result.type).toEqual('div');
in my case, if I log the result it is:
LOG: Object{type: function PlayerProfile() {..}, .. }
so I changed my test like:
expect(result.type).toEqual(PlayerProfile)
now it gives me this error:
Assertion Error: expected [Function: PlayerProfile] to equal [Function: withMUI]
So PlayerProfile's type is the higher order function withMUI.
PlayerProfile decorated with withMUI, using shallow rendering, only the PlayerProfile component is rendered and not it's children. So shallow rendering wouldn't work with decorated components I assume.
My question is:
Why in the tutorial result.type is expected to be a div, but in my case isn't.
How can I test a React component decorated with higher order component using shallow rendering?
You can't. First let's slightly desugar the decorator:
let PlayerProfile = withMUI(
class PlayerProfile extends React.Component {
// ...
}
);
withMUI returns a different class, so the PlayerProfile class only exists in withMUI's closure.
This is here's a simplified version:
var withMUI = function(arg){ return null };
var PlayerProfile = withMUI({functionIWantToTest: ...});
You pass the value to the function, it doesn't give it back, you don't have the value.
The solution? Hold a reference to it.
// no decorator here
class PlayerProfile extends React.Component {
// ...
}
Then we can export both the wrapped and unwrapped versions of the component:
// this must be after the class is declared, unfortunately
export default withMUI(PlayerProfile);
export let undecorated = PlayerProfile;
The normal code using this component doesn't change, but your tests will use this:
import {undecorated as PlayerProfile} from '../src/PlayerProfile';
The alternative is to mock the withMUI function to be (x) => x (the identity function). This may cause weird side effects and needs to be done from the testing side, so your tests and source could fall out of sync as decorators are added.
Not using decorators seems like the safe option here.
Use Enzyme to test higher order / decorators with Shallow
with a method called dive()
Follow this link, to see how dive works
https://github.com/airbnb/enzyme/blob/master/docs/api/ShallowWrapper/dive.md
So you can shallow the component with higher order and then dive inside.
In the above example :
const wrapper=shallow(<PlayerProfile name={name} avatar={}/>)
expect(wrapper.find("PlayerProfile").dive().find(".player-profile").length).toBe(1)
Similarly you can access the properties and test it.
You can use 'babel-plugin-remove-decorators' plugin. This solution will let you write your components normally without exporting decorated and un-decorated components.
Install the plugin first, then create a file with the following content, let us call it 'babelTestingHook.js'
require('babel/register')({
'stage': 2,
'optional': [
'es7.classProperties',
'es7.decorators',
// or Whatever configs you have
.....
],
'plugins': ['babel-plugin-remove-decorators:before']
});
and running your tests like below will ignore the decorators and you will be able to test the components normally
mocha ./tests/**/*.spec.js --require ./babelTestingHook.js --recursive
I think the above example is confusing because the decorator concept is used interchangeably with idea of a "higher order component". I generally use them in combination which will make testing/rewire/mocking easier.
I would use decorator to:
Provide props to a child component, generally to bind/listen to a flux store
Where as I would use a higher order component
to bind context in a more declarative way
The problem with rewiring is I don't think you can rewire anything that is applied outside of the exported function/class, which is the case for a decorator.
If you wanted to use a combo of decorators and higher order components you could do something like the following:
//withMui-decorator.jsx
function withMUI(ComposedComponent) {
return class withMUI extends Component {
constructor(props) {
super(props);
this.state = {
store1: ///bind here based on some getter
};
}
render() {
return <ComposedComponent {...this.props} {...this.state} {...this.context} />;
}
};
}
//higher-order.jsx
export default function(ChildComp) {
#withMui //provide store bindings
return class HOC extends Component {
static childContextTypes = {
getAvatar: PropTypes.func
};
getChildContext() {
let {store1} = this.props;
return {
getAvatar: (id) => ({ avatar: store1[id] });
};
}
}
}
//child.js
export default Child extends Component {
static contextTypes = {
getAvatar: PropTypes.func.isRequired
};
handleClick(id, e) {
let {getAvatar} = this.context;
getAvatar(`user_${id}`);
}
render() {
let buttons = [1,2,3].map((id) => {
return <button type="text" onClick={this.handleClick.bind(this, id)}>Click Me</button>
});
return <div>{buttons}</div>;
}
}
//index.jsx
import HOC from './higher-order';
import Child from './child';
let MyComponent = HOC(Child);
React.render(<MyComponent {...anyProps} />, document.body);
Then when you want to test you can easily "rewire" your stores supplied from the decorator because the decorator is inside of the exported higher order component;
//spec.js
import HOC from 'higher-order-component';
import Child from 'child';
describe('rewire the state', () => {
let mockedMuiDecorator = function withMUI(ComposedComponent) {
return class withMUI extends Component {
constructor(props) {
super(props);
this.state = {
store1: ///mock that state here to be passed as props
};
}
render() {
//....
}
}
}
HOC.__Rewire__('withMui', mockedMuiDecorator);
let MyComponent = HOC(Child);
let child = TestUtils.renderIntoDocument(
<MyComponent {...mockedProps} />
);
let childElem = React.findDOMNode(child);
let buttons = childElem.querySelectorAll('button');
it('Should render 3 buttons', () => {
expect(buttons.length).to.equal(3);
});
});
I'm pretty sure this doesn't really answer your original question but I think you are having problems reconciling when to use decorators vs.higher order components.
some good resources are here:
http://jaysoo.ca/2015/06/09/react-contexts-and-dependency-injection/
https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
https://github.com/badsyntax/react-seed/blob/master/app/components/Menu/tests/Menu-test.jsx
https://github.com/Yomguithereal/baobab-react/blob/master/test/suites/higher-order.jsx
In my case decorators are very useful and I dont want to get rid of them (or return wrapped and unwrapped versions) im my application.
The best way to do this in my opinion is to use the babel-plugin-remove-decorators (which can be used to remove them in tests) has Qusai says, but I wrote the pre-processor differently like below:
'use strict';
var babel = require('babel-core');
module.exports = {
process: function(src, filename) {
// Ignore files other than .js, .es, .jsx or .es6
if (!babel.canCompile(filename)) {
return '';
}
if (filename.indexOf('node_modules') === -1) {
return babel.transform(src, {
filename: filename,
plugins: ['babel-plugin-remove-decorators:before']
}).code;
}
return src;
}
};
Take notice of the babel.transform call that im passing the babel-plugin-remove-decorators:before element as an array value, see: https://babeljs.io/docs/usage/options/
To hook this up with Jest (which is what I used), you can do it with settings like below in your package.json:
"jest": {
"rootDir": "./src",
"scriptPreprocessor": "../preprocessor.js",
"unmockedModulePathPatterns": [
"fbjs",
"react"
]
},
Where preprocessor.js is the name of the preprocessor.