Yone
Yone

Reputation: 2134

REACT: How could we use an external library as a script, into a class or functional component? Is it possible?

Hello and than you for reading this question!

I have a use case which I have to load a TIFF image.

So then I have researched how to use this library: https://github.com/seikichi/tiff.js

In addition, there is a great example of its use, thanks to @K3N Display Existing TIFF File Using the seikichi/tiff Library

The difficulty I am facing is trying to integrate the previous use example with React.

I have tried the following:

To create a class component which loads the script and appends it to the document:

LoadTIFF.js

import React from 'react';

class LoadTIFF extends React.Component {
    componentWillMount() {
        const script = document.createElement("script");

        script.src = "https://cdn.rawgit.com/seikichi/tiff.js/master/tiff.min.js";
        script.async = true;

        document.body.appendChild(script);
    }

    render() {
        return (
            document.querySelector("input").onchange = function () {

                // convert File blob to ArrayBuffer using a FileReader
                var fileReader = new FileReader();

                fileReader.onload = function () {                     // file is now ArrayBuffer:
                    var tiff = new Tiff({buffer: this.result});        // parse and convert
                    var canvas = tiff.toCanvas();                      // convert to canvas
                    document.querySelector("div").appendChild(canvas); // show canvas with content
                };

                fileReader.readAsArrayBuffer(this.files[0]);         // convert selected file
            }
        );

    }
}

export {LoadTIFF};

And then I have tried to use it from a parent component which aim is to display a title and a canvas, Canvas.js:

import React from 'react';
import {LoadTIFF} from "./LoadTIFF";


class Canvas extends React.Component {

    //A canvas to display images with a title

    render() {
        return (
            <div className="previewComponent">
                <div className="imgPreview">
                    {this.props.title}
                    <LoadTIFF/>
                    <canvas></canvas>
                </div>
            </div>
        )
    }
}

export {Canvas};

And the console's output is: πŸ™ƒ

Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
    in LoadTIFF (at Canvas.js:14)
    in div (at Canvas.js:12)
    in div (at Canvas.js:11)
    in Canvas (at CanvasHorizontalSplitterLayout.js:12)
    in div (at CanvasHorizontalSplitterLayout.js:11)
    in div (created by o)
    in o (created by t)
    in div (created by t)
    in t (at CanvasHorizontalSplitterLayout.js:10)
    in CanvasHorizontalSplitterLayout (at Caballo.js:35)
    in div (at Caballo.js:34)
    in div (created by o)
    in o (created by t)
    in div (created by t)
    in t (at Caballo.js:33)
    in main (at Caballo.js:31)
    in Caballo (created by Route)
    in Route (at Main.js:14)
    in Switch (at Main.js:12)
    in main (at Main.js:11)
    in Main (at index.js:17)
    in div (at index.js:15)
    in App (at index.js:24)
    in Router (created by BrowserRouter)
    in BrowserRouter (at index.js:23)
__stack_frame_overlay_proxy_console__ @ index.js:2178
printWarning @ warning.js:33
warning @ warning.js:57
warnOnFunctionType @ react-dom.development.js:6760
reconcileChildFibers @ react-dom.development.js:7664
reconcileChildrenAtExpirationTime @ react-dom.development.js:7756
reconcileChildren @ react-dom.development.js:7747
finishClassComponent @ react-dom.development.js:7881
updateClassComponent @ react-dom.development.js:7850
beginWork @ react-dom.development.js:8225
performUnitOfWork @ react-dom.development.js:10224
workLoop @ react-dom.development.js:10288
callCallback @ react-dom.development.js:542
invokeGuardedCallbackDev @ react-dom.development.js:581
invokeGuardedCallback @ react-dom.development.js:438
renderRoot @ react-dom.development.js:10366
performWorkOnRoot @ react-dom.development.js:11014
performWork @ react-dom.development.js:10967
requestWork @ react-dom.development.js:10878
scheduleWorkImpl @ react-dom.development.js:10732
scheduleWork @ react-dom.development.js:10689
scheduleTopLevelUpdate @ react-dom.development.js:11193
updateContainer @ react-dom.development.js:11231
(anonymous) @ react-dom.development.js:15226
unbatchedUpdates @ react-dom.development.js:11102
renderSubtreeIntoContainer @ react-dom.development.js:15225
render @ react-dom.development.js:15290
./src/index.js @ index.js:22
__webpack_require__ @ bootstrap 5c657e191a78fa604491:678
fn @ bootstrap 5c657e191a78fa604491:88
0 @ index.js:23
__webpack_require__ @ bootstrap 5c657e191a78fa604491:678
./node_modules/ansi-regex/index.js.module.exports @ bootstrap 5c657e191a78fa604491:724
(anonymous) @ bootstrap 5c657e191a78fa604491:724

Could you help me please? πŸ™ƒ

I have also read:

Adding script tag to React/JSX

Thank you for your help!

EDIT1:

Thanks to @Dragoş Paul Marinescu and specially to @Boy With Silver Wings πŸ˜‰πŸ˜‰πŸ˜‰ I have tried @Boy With Silver Wings's example in a new React app, as you can see here: https://github.com/YoneMoreno/ReadTiffReactApp

The console output is the following:

./src/App.js
  Line 14:  Unexpected use of 'event'  no-restricted-globals
  Line 16:  'Tiff' is not defined      no-undef

Search for the keywords to learn more about each error.
This error occurred during the build time and cannot be dismissed.

About the event usage, it looks like we can not use it as a global variable so instead it should be used from function's props' events: Unexpected use of 'event' no-restricted-globals when using event.target.id to get id from bind(this)

So we should pass event as a prop to onChange:

onChange(event) {
        const canvasNode = this.canvas;
        const fileReader = new FileReader();

        fileReader.readAsArrayBuffer(event.target.files[0]);
        fileReader.onload = function () {
            const tiff = new Tiff({
                buffer: this.result
            });
            const canvas = tiff.toCanvas();
            canvasNode.appendChild(canvas);
        };

    }

For the second issue, it looks like it needs to be declared a a global explicity:

My script for react-google-maps don't loads

With those two changes it works well.πŸ˜‰

Upvotes: 1

Views: 1722

Answers (1)

Agney
Agney

Reputation: 19224

Load the function in onChange handler and put the corresponding html in render() method:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.onChange = this.onChange.bind(this);
  }
  onChange(file) {
    const canvasNode = this.canvas;
    const fileReader = new FileReader();
    
    fileReader.readAsArrayBuffer(event.target.files[0]);
    fileReader.onload = function() {
      const tiff = new Tiff({
        buffer: this.result
      });
      const canvas = tiff.toCanvas();
      canvasNode.appendChild(canvas);
    };
  
  }
  render() {
    return ( 
      <div>
        <label>
         Select TIFF file:
         <input type="file" onChange={this.onChange} />
        </label>
        <span ref={(canvas)=>this.canvas=canvas}/>
      </div>
    );
  }
}

ReactDOM.render( < App / > , document.getElementById("root"));
<script src="https://cdn.rawgit.com/seikichi/tiff.js/master/tiff.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root"></div>

We are using ref which is the React recommended way to selecting nodes from the DOM if we needed to do that.

Upvotes: 4

Related Questions