Chris
Chris

Reputation: 31206

React/NPM: How to encapsulate a component?

Suppose I have the following working react app that displays a spinning cube.

npm create-react-app
vi src/App.js

Copy-paste the content below:

import React, { Component } from "react";
import ReactDOM from "react-dom";
import * as THREE from "three";
class App extends Component {
  componentDidMount() {
    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
    var renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    // document.body.appendChild( renderer.domElement );
    // use ref as a mount point of the Three.js scene instead of the document.body
    this.mount.appendChild( renderer.domElement );
    var geometry = new THREE.BoxGeometry( 1, 1, 1 );
    var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
    var cube = new THREE.Mesh( geometry, material );
    scene.add( cube );
    camera.position.z = 5;
    var animate = function () {
      requestAnimationFrame( animate );
      cube.rotation.x += 0.01;
      cube.rotation.y += 0.01;
      renderer.render( scene, camera );
    };
    animate();
  }
  render() {
    return (
      <div ref={ref => (this.mount = ref)} />
    )
  }
}
export default App
npm run build

And voila, spinning cube!


But, the very next thing I try to do is: encapsulate the spinning cube and create some drop-down that allows me to view a cube or something else.

I didn't get as far as a drop-down though, because I could not encapsulate the Cube class beneath a function:

mport React, { Component } from "react";
import ReactDOM from "react-dom";
import * as THREE from "three";
class ICube extends Component {
  componentDidMount() {
    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
    var renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    // document.body.appendChild( renderer.domElement );
    // use ref as a mount point of the Three.js scene instead of the document.body
    this.mount.appendChild( renderer.domElement );
    var geometry = new THREE.BoxGeometry( 1, 1, 1 );
    var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
    var cube = new THREE.Mesh( geometry, material );
    scene.add( cube );
    camera.position.z = 5;
    var animate = function () {
      requestAnimationFrame( animate );
      cube.rotation.x += 0.01;
      cube.rotation.y += 0.01;
      renderer.render( scene, camera );
    };
    animate();
  }
  render() {
    return (
      <div ref={ref => (this.mount = ref)} />
    )
  }
}
export default function App() {
  var cube = ICube()
  return ( 
      <div ref={ref => (cube.mount = ref)} />
  )   
}

This builds, but does not render. I am not sure how this encapsulation is supposed to proceed. Is there some particular way to encapsulate this?

I have also tried creating a:

export default class App extends Component {
  cube = ICube()
  componentDidMount() {
  }
  render() {
    return (
      <div ref={ref => (this.cube.mount = ref)} />
    )
  }
}

As a direct encapsulation, but that did not work.

Upvotes: 1

Views: 687

Answers (2)

98sean98
98sean98

Reputation: 352

If you simply wanted to make ICube render within another component. Simply do

// class component
export default class App extends Component {
  render() {
    return (
      <div>
        <ICube />
        {/* more components */}
      </div>
    )
  }
}

// functional components
export default function App() {
  return (
    <div>
      <ICube />
      {/* more components */}
    </div>
  );
}

It's not a usual pattern for React to do cube = ICube() when using class components. See React docs. And composition can be done using a mixture of class components and functional components any way you like.

Upvotes: 1

Damodar Hegde
Damodar Hegde

Reputation: 468

You don't need to create refs for this purpose. Simply import your component and use it in your render function

import React, { Component } from "react";
import ReactDOM from "react-dom";
import * as THREE from "three";
class ICube extends Component {
  componentDidMount() {
    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
    var renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    // document.body.appendChild( renderer.domElement );
    // use ref as a mount point of the Three.js scene instead of the document.body
    this.mount.appendChild( renderer.domElement );
    var geometry = new THREE.BoxGeometry( 1, 1, 1 );
    var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
    var cube = new THREE.Mesh( geometry, material );
    scene.add( cube );
    camera.position.z = 5;
    var animate = function () {
      requestAnimationFrame( animate );
      cube.rotation.x += 0.01;
      cube.rotation.y += 0.01;
      renderer.render( scene, camera );
    };
    animate();
  }
  render() {
    return (
      <div className"some-style-class">
         <App />
         {//some other components}
      </div>
    )
  }
}

To render your shape conditionally, you can pass props to your App component like

<App
  shape={this.state.shape} 
/>

The value for the type of shape can come from the drop down component you make

Upvotes: 1

Related Questions