Reputation: 1200
PROBLEM:
I'm trying to make an example from react-cropper
work in TypeScript. The example works fine with .JSX and there are Typings for it, so I thought it would just work sublime, with some changes to the .JSX code.
One of the tricky things to get my head around was the reference pattern in TypeScript and React. However, I found this amazing question on SO which I've followed to set everything up.
EXPECTED:
That the ref-pattern would work as intended and I'd have access to the underlying functions (from the object).
I.e. I'm expecting to see that this.cropRef
has access to getCroppedCanvas
(defined in react-cropper
).
ACTUAL:
console.log(this)
in the cropImage()
function shows that refs
are empty, even though I'm following the above example.
Trying to use the now deprecated ref-pattern of using strings show me that this
has the reference heading into the cropImage()
function. However, because TS is strongly typed, I dont' have access to getCroppedCanvas()
.
CODE:
import * as React from "react";
import Cropper from 'react-cropper';
import 'cropperjs/dist/cropper.css';
export class ImageEditor extends React.Component<any, any> {
private cropRef : React.RefObject<Cropper>;
constructor(props : any) {
super(props);
this.state = {
src : "",
cropResult : ""
};
this.cropRef = React.createRef();
this.cropImage = this.cropImage.bind(this);
this.onChange = this.onChange.bind(this);
}
onChange(e : any) {
e.preventDefault();
let files;
if (e.dataTransfer) {
files = e.dataTransfer.files;
} else if (e.target) {
files = e.target.files;
}
const reader = new FileReader();
reader.onload = () => {
this.setState({ src : reader.result });
};
reader.readAsDataURL(files[0]);
}
cropImage() {
console.log(this.refs);
if (typeof this.cropRef.current.getCroppedCanvas() === 'undefined') {
return;
}
this.setState({
cropResult: this.cropRef.current.getCroppedCanvas().toDataURL(),
});
}
render() {
return (
<div>
<div style={{ width: '100%' }}>
<input type="file" onChange={this.onChange} />
<br />
<br />
<Cropper
style={{ height: 400, width: '100%' }}
aspectRatio={16 / 9}
preview=".img-preview"
guides={false}
src={this.state.src}
ref={(cropper : any) => { this.cropRef = cropper}}
/>
</div>
<div>
<div className="box" style={{ width: '50%', float: 'right' }}>
<h1>Preview</h1>
<div className="img-preview" style={{ width: '100%', float: 'left', height: 300 }} />
</div>
<div className="box" style={{ width: '50%', float: 'right' }}>
<h1>
<span>Crop</span>
<button onClick={this.cropImage} style={{ float: 'right' }}>
Crop Image
</button>
</h1>
<img style={{ width: '100%' }} src={this.state.cropResult} alt="cropped image" />
</div>
</div>
</div>
);
}
}
EDIT:
edited index.d.ts for react-cropper:
import * as cropperjs from 'cropperjs';
import * as React from 'react';
import Data = cropperjs.Data;
import ContainerData = cropperjs.ContainerData;
import ImageData = cropperjs.ImageData;
import CanvasData = cropperjs.CanvasData;
import CropBoxData = cropperjs.CropBoxData;
import CroppedCanvasOptions = cropperjs.CroppedCanvasOptions;
type ReactCropperProps = cropperjs.CropperOptions & React.AllHTMLAttributes<HTMLImageElement>;
interface ReactCropper extends cropperjs {} // tslint:disable-line no-empty-interface
declare class ReactCropper extends React.Component<ReactCropperProps> {
on(eventname: string, callback: () => void): void;
}
export default ReactCropper;
package.json
{
"name": "name",
"version": "1.0.0",
"description": "packages for content",
"main": "webpack.config.js",
"scripts": {
"build": "webpack",
"test": "karma start karma.unit.build.js",
"build-test": "karma start karma.unit.js",
"coverage": "karma start karma.coverage.js"
},
"author": "Joakim Bajoul Kakaei",
"license": "ISC",
"devDependencies": {
"@types/chai": "^4.1.4",
"@types/mocha": "^5.2.5",
"@types/react": "^16.4.7",
"@types/react-cropper": "./node_modules/react-cropper.fixed",
"@types/react-dom": "^16.0.6",
"@types/react-dropzone": "^4.2.0",
"@types/sinon": "^5.0.1",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"chai": "^4.1.2",
"css-loader": "^1.0.0",
"istanbul-instrumenter-loader": "^3.0.1",
"karma": "^2.0.5",
"karma-chai": "^0.1.0",
"karma-coverage": "^1.1.2",
"karma-mocha": "^1.3.0",
"karma-phantomjs-launcher": "^1.0.4",
"karma-sinon": "^1.0.5",
"karma-typescript-preprocessor2": "^1.2.1",
"karma-webpack": "^3.0.0",
"mocha": "^5.2.0",
"mocha-webpack": "^1.1.0",
"phantomjs-prebuilt": "^2.1.16",
"sinon": "^6.1.4",
"style-loader": "^0.21.0",
"ts-loader": "^4.4.2",
"ts-node": "^7.0.0",
"typescript": "^2.9.2",
"typings": "^2.1.1",
"webpack": "^4.16.2",
"webpack-cli": "^3.1.0"
},
"dependencies": {
"npm": "^6.3.0",
"react": "^16.4.1",
"react-cropper": "^1.0.1",
"react-dom": "^16.4.1",
"react-dropzone": "^4.2.13"
}
}
node_modules/react-cropper/package.json:
{
"name": "@types/react-cropper",
"dependencies": {
}
}
Upvotes: 1
Views: 1954
Reputation: 30949
It looks like you are using a strange mix of the callback pattern and the RefObject
pattern. If you want to use the RefObject
, you should just pass the RefObject
as the ref
attribute, i.e.:
ref={this.cropRef}
If you want to use a callback, then the declaration should be:
private cropRef : Cropper;
and you would use this.cropRef.getCroppedCanvas()
rather than this.cropRef.current.getCroppedCanvas()
.
Upvotes: 1