Question clarification:
Specifically, Id like to know how the example below can be written without typescript. Understanding how to declare properties on classes etc. would be useful to me. I believe it should be possible to use blueprintjs (which is written in typescript), without using typescript in my implementations.
I'm following docs at:http://blueprintjs.com/docs/#components.toaster.js.react
There is sample code like this:
import { Button, Position, Toaster } from "#blueprintjs/core";
class MyComponent extends React.Component {
private toaster: Toaster;
private refHandlers = {
toaster: (ref: Toaster) => this.toaster = ref,
};
public render() {
return (
<div>
<Button onClick={this.addToast} text="Procure toast" />
<Toaster position={Position.TOP_RIGHT} ref={this.refHandlers.toaster} />
</div>
)
}
private addToast = () => {
this.toaster.show({ message: "Toasted!" });
}
}
But I get Syntax Error: Unexpected token, expected ( (5:16) which is the 'toaster' after 'private'. I am not precompiling typescript. I'm using es6 with webpack. How would I re-write this sample code to work with my environment?
Thanks.
blueprintjs is implemented using typescript, and that example is using typescript syntax.
See: http://blueprintjs.com/docs/#TypeScript
Here's how I did it. I got a bit of help from webpack based on this tutorial: https://www.typescriptlang.org/docs/handbook/react-&-webpack.html. I set the target in the tsconfig.json configuration file to es6, and this is what webpack compiled to (more or less).
Any manual improvements to this solution welcome.
import {Button, Position, Toaster} from "#blueprintjs/core";
class ToastLauncher extends React.Component {
constructor(props) {
super(props);
this.refHandlers = {
toaster: (ref) => this.toaster = ref,
};
this.addToast = () => {
this.toaster.show({ message: "Toasted!" });
};
}
render() {
return (
<div>
<Button onClick={this.addToast} text="Procure toast" />
<Toaster position={Position.TOP_RIGHT} ref={this.refHandlers.toaster} />
</div>
)
}
}
export default ToastLauncher
Related
I am facing this error while using sonar-scanner.
It is unable to parse all those files in which i have used arrow functions.
import React from "react";
import { Button } from "antd";
import history from "src/components/history";
class BackButton extends React.Component {
handleClick = () => {
history.goBack();
if (this.props.onBack) {
this.props.onBack();
}
};
render() {
return <Button icon="arrow-left" onClick={this.handleClick} />;
}
}
export default BackButton;
The error at line 6.
Need a solution to fix this.
in order to use arrow functions inside a class you need to enable this plugin in your babel configuration.
{
"plugins": [
"transform-class-properties"
]
}
or you can do it like this
class BackButton extends React.Component {
constructor() {
super();
this.handleClick = (val) => {
...
};
…
}
}
I suspect the problem isn't arrow functions, it's class fields. Your handleClick is a class field (basically a property declaration) using an arrow function as its initializer. The class fields proposal is mature, but still at Stage 3, not actually in the spec yet (not even ES2020). In contrast, arrow functions have been in the language for over five years.
You'll need to be sure to enable support for class fields.
Below code failed with jest, I got unexpected token error
export default class Search extends Component {
state = { // problem is here
q: ''
}
onChange = e => {
this.setState({
q: e.target.value
})
}
render() {
return (
<div>
<input onChange={this.onChange} placeholder="search" />
</div>
)
}
}
I'm using babel-7, any plugin I need to pull in for jest?
I'm using jest version ^23.6.0
You need to add babel-plugin-transform-class-properties if you want to use class properties. So install it then make sure you add it as a plugin in your .babelrc file like this:
"plugins": [
"#babel/plugin-proposal-class-properties"
]
Either use a constructor and define your state with this.state:
constructor(props) {
super(props)
this.state = {
q: ''
}
}
or use https://www.npmjs.com/package/babel-plugin-transform-class-properties.
Class fields are still a stage 3 proposal https://github.com/tc39/proposal-class-fields
I've tried using componentWillMount and componentDidMount to initialize CKEditor from within the context of React, but it doesn't seem to work no matter what combination I try. Has anyone found a solution to this besides switching editors?
I published a package on Npm for using CKEditor with React. It takes just 1 line of code to integrate in your project.
Github link - https://github.com/codeslayer1/react-ckeditor.
How to Use?
Install the package using npm install react-ckeditor-component --save.
Then include the component in your React app and pass it your content and any other props that you need(all props listed on Github page) -
<CKEditor activeClass="editor" content={this.state.content} onChange={this.updateContent} />
The package uses the default build of CKEditor but you can use a custom build as well along with any of the plugins you like. It also includes a sample application. Hope you will find it useful.
Sage describes an awesome solution in his answer. It was a lifesaver, as I've only just started using React, and I needed it to get this going. I did, however, change the implementation, also incorporating Jared's suggestions (using componentDidMount). Also, my need was to have a change callback, like so:
Usage of the component:
<CKEditor value={this.props.value} onChange={this.onChange}/>
Added this to index.html:
<script src="//cdn.ckeditor.com/4.6.1/basic/ckeditor.js"></script>
Using the following component code:
import React, {Component} from "react";
export default class CKEditor extends Component {
constructor(props) {
super(props);
this.componentDidMount = this.componentDidMount.bind(this);
}
render() {
return (
<textarea name="editor" cols="100" rows="6" defaultValue={this.props.value}></textarea>
)
}
componentDidMount() {
let configuration = {
toolbar: "Basic"
};
CKEDITOR.replace("editor", configuration);
CKEDITOR.instances.editor.on('change', function () {
let data = CKEDITOR.instances.editor.getData();
this.props.onChange(data);
}.bind(this));
}
}
Again, all credits to Sage!
The following is an improved version of the basic version above, which supports multiple CKEditor instances on the same page:
import React, {Component} from "react";
export default class CKEditor extends Component {
constructor(props) {
super(props);
this.elementName = "editor_" + this.props.id;
this.componentDidMount = this.componentDidMount.bind(this);
}
render() {
return (
<textarea name={this.elementName} defaultValue={this.props.value}></textarea>
)
}
componentDidMount() {
let configuration = {
toolbar: "Basic"
};
CKEDITOR.replace(this.elementName, configuration);
CKEDITOR.instances[this.elementName].on("change", function () {
let data = CKEDITOR.instances[this.elementName].getData();
this.props.onChange(data);
}.bind(this));
}
}
Please note that this requires some unique ID to be passed along as well:
<CKEditor id={...} value={this.props.value} onChange={this.onChange}/>
This is for a React component which displays a P paragraph of text. If the user wants to edit the text in the paragraph, they can click it which will then attach a CKEditor instance. When the user is done altering the text in the Editor instance, the "blur" event fires which transfers the CKEditor data to a state property and destroys the CKEditor Instance.
import React, {PropTypes, Component} from 'react';
export default class ConditionalWYSIWYG extends Component {
constructor(props) {
super(props);
this.state = {
field_name:this.props.field_name,
field_value:this.props.field_value,
showWYSIWYG:false
};
this.beginEdit = this.beginEdit.bind(this);
this.initEditor = this.initEditor.bind(this);
}
render() {
if ( this.state.showWYSIWYG ) {
var field = this.state.field_name;
this.initEditor(field);
return (
<textarea name='editor' cols="100" rows="6" defaultValue={unescape(this.state.field_value)}></textarea>
)
} else {
return (
<p className='description_field' onClick={this.beginEdit}>{unescape(this.state.field_value)}</p>
)
}
}
beginEdit() {
this.setState({showWYSIWYG:true})
}
initEditor(field) {
var self = this;
function toggle() {
CKEDITOR.replace("editor", { toolbar: "Basic", width: 870, height: 150 });
CKEDITOR.instances.editor.on('blur', function() {
let data = CKEDITOR.instances.editor.getData();
self.setState({
field_value:escape(data),
showWYSIWYG:false
});
self.value = data;
CKEDITOR.instances.editor.destroy();
});
}
window.setTimeout(toggle, 100);
}
}
The self.value = data allows me to retrieve the text from the parent component via a simple ref
The window.setTimeout(); gives React time to do what it does. Without this delay, I would get an Cannot read property 'getEditor' of undefined error in the console.
Hope this helps
Just refer the ckeditor.js in index.html, and use it with window.CKEDITOR. Don't use CKEDITOR straight like the document in React component.
Just read the first-line of ckeditor.js, you will find what about define of CKEDITOR.
Thanks to Sage, Sander & co. I just wanted to contribute a version for the "inline" mode of CKEditor.
First, disable CKEditor's "auto-inline" behavior with...
CKEDITOR.disableAutoInline = true
Then, for the actual component...
import React, {Component} from 'react';
export default class CKEditor extends Component {
constructor(props) {
super(props);
this.elementName = "editor_" + this.props.id;
this.componentDidMount = this.componentDidMount.bind(this);
this.onInput = this.onInput.bind(this);
}
onInput(data) {
console.log('onInput: ' + data);
}
render() {
return (
<div
contentEditable={true}
suppressContentEditableWarning
className="rte"
id={this.elementName}>
{this.props.value}</div>
)
}
componentDidMount() {
let configuration = {
toolbar: "Basic"
};
CKEDITOR.inline(this.elementName, configuration);
CKEDITOR.instances[this.elementName].on("change", function() {
let data = CKEDITOR.instances[this.elementName].getData();
this.onInput(data);
}.bind(this));
}
}
Usage would be something like this:
<CKEditor id="102" value="something" onInput={this.onInput} />
I'm in the process of learning React. I'm going through a tutorial series that uses ES5. I'm trying to write my components in ES6, which seemed like a straightforward enough process when I was looking at React's documentation about it.
This is the code that is giving me problems:
import React from 'react';
import Button from './button';
import ListItem from './list-item';
export default class DropDown extends React.Component {
constructor() {
super();
this.state = {open: false};
}
handleClick() {
this.state.open = true;
}
render() {
var list = this.props.items.map((item) => {
return <ListItem item={item}/>
});
return (
<div className="dropdown">
<Button onClick={this.handleClick} className='btn-default' title={this.props.title}
subTitleClassName='caret'/>
<ul className={'dropdown-menu ' + (this.state.open ? "show" : "") }>
{list}
</ul>
</div>
)
}
}
I get a TypeError: Cannot read property 'state' of undefined at
handleClick() {
this.state.open = true;
}
whenever I click the button in Chrome.
Could anyone tell me why this is undefined, or what I'm doing wrong?
I should probably mention that I'm using Babelify to transpile it to ES5 as part of my gulp/browserify build process.
The reason why you are getting this error is because 'this' is not autobound for us in es6, as it is in es5 createClass. We can use bind to fix this problem or we can use an arrow function. In your button element, try the following:
<Button
onClick={(e) => this.handleClick(e)}
className='btn-default'
title={this.props.title}
subTitleClassName='caret'
/>
As it was mentioned in the accepted answer, the problem is in binding function to component instance. However, the easiest approach with arrow function isn't always considered the best practice due to creating a new function for Button's prop on every render (that might be sensitive in some optimisation approaches). So you may use binding-in-constructor approach instead:
class Foo extends Component {
constructor(props) {
super(props);
this.methodName = this.methodName.bind(this);
}
methodName(e) {
// handle click
}
render() {
return <Button onClick={this.methodName} />;
}
}
This way you keep render method, which is called by react pretty often, cleaner from build-up code.
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.