React-Three-Renderer refs не текущий в componentDidUpdate (mvce включен)

я использую react-three-renderer (npm, github) для построения сцены с три.js.

у меня проблема, которую я свел к MVCE. Refs не обновляются в последовательности, которую я ожидаю от них. Во-первых, вот основной код для просмотра:

var React = require('react');
var React3 = require('react-three-renderer');
var THREE = require('three');
var ReactDOM = require('react-dom');

class Simple extends React.Component {
  constructor(props, context) {
    super(props, context);

    // construct the position vector here, because if we use 'new' within render,
    // React will think that things have changed when they have not.
    this.cameraPosition = new THREE.Vector3(0, 0, 5);

    this.state = {
      shape: 'box'
    };

    this.toggleShape = this.toggleShape.bind(this);
  }

  toggleShape() {
    if(this.state.shape === 'box') {
      this.setState({ shape: 'circle' });
    } else {
      this.setState({ shape: 'box' });
    }
  }

  renderShape() {
    if(this.state.shape === 'box') {
      return <mesh>
        <boxGeometry
          width={1}
          height={1}
          depth={1}
          name='box'
          ref={
            (shape) => {
              this.shape = shape;
              console.log('box ref ' + shape);
            }
          }
        />
        <meshBasicMaterial
          color={0x00ff00}
        />
      </mesh>;
    } else {
      return <mesh>
        <circleGeometry
          radius={2}
          segments={50}
          name='circle'
          ref={
            (shape) => {
              this.shape = shape;
              console.log('circle ref ' + shape);
            }
          }
        />
        <meshBasicMaterial
          color={0x0000ff}
        />
      </mesh>
    }
  }

  componentDidUpdate() {
    console.log('componentDidUpdate: the active shape is ' + this.shape.name);
  }

  render() {
    const width = window.innerWidth; // canvas width
    const height = window.innerHeight; // canvas height

    var position = new THREE.Vector3(0, 0, 10);
    var scale = new THREE.Vector3(100,50,1);

    var shape = this.renderShape();

    return (<div>
        <button onClick={this.toggleShape}>Toggle Shape</button>
        <React3
          mainCamera="camera"
          width={width}
          height={height}
          onAnimate={this._onAnimate}>
          <scene>
            <perspectiveCamera
              name="camera"
              fov={75}
              aspect={width / height}
              near={0.1}
              far={1000}
              position={this.cameraPosition}/>
            {shape}
          </scene>
        </React3>
    </div>);
  }
}

ReactDOM.render(<Simple/>, document.querySelector('.root-anchor'));

это отображает базовую сцену с зеленой коробкой, вилкой примера на целевой странице GitHub react-three-renderer. Кнопка в левом верхнем углу переключает форму в сцене должен быть синий круг, и если щелкнуть снова, вернитесь к зеленому ящику. Я делаю некоторые регистрации в обратных вызовах ref и в componentDidUpdate. Вот где происходит суть проблемы, с которой я сталкиваюсь. После первого нажатия кнопки переключения я ожидаю, что ссылка на фигуру будет указывать на круг. Но как вы можете видеть из журнала, в componentDidUpdate ссылка по-прежнему указывает на окно:

componentDidUpdate: активная форма box

вход в строки после этого показывает, что обратные вызовы ref попали

box ref null [React вызывает null на старом ref для предотвращения утечек памяти]

circle ref [object Object]

вы можете отбросить точки останова для проверки и проверки. Я ожидал бы, что эти две вещи произойдут до того, как мы войдем componentDidUpdate, но, как видите, это происходит в обратном порядке. Почему так? Есть основной вопрос react-three-renderer (если да, можете ли вы диагностировать его?), или я неправильно реагирую refs?

MVCE доступен в этот репозиторий github. Скачайте его, запустите npm install, и открыть _dev / public / home.формат html.

спасибо заранее.

1 ответов


Я проверил источник в react-three-renderer. В lib / React3.jsx, существует двухфазный рендеринг.

componentDidMount() {
    this.react3Renderer = new React3Renderer();
    this._render();
  }

  componentDidUpdate() {
    this._render();
  }

метод _render-это тот, который, кажется, загружает дочерние объекты-объекты сетки в пределах трех.

_render() {
    const canvas = this._canvas;

    const propsToClone = { ...this.props };

    delete propsToClone.canvasStyle;

    this.react3Renderer.render(
      <react3
        {...propsToClone}
        onRecreateCanvas={this._onRecreateCanvas}
      >
        {this.props.children}
      </react3>, canvas);
  }

метод render рисует холст и не заполняет дочерние элементы или не вызывает три.

  render() {
    const {
      canvasKey,
    } = this.state;

    return (<canvas
      ref={this._canvasRef}
      key={canvasKey}
      width={this.props.width}
      height={this.props.height}
      style={{
        ...this.props.canvasStyle,
        width: this.props.width,
        height: this.props.height,
      }}
    />);
  }

Резюмируя, это последовательность:

  1. вызывается рендеринг компонента приложения.
  2. это делает react-три-рендерер, который вытягивает холст.
  3. componentDidUpdate компонента приложения вызывается.
  4. вызывается componentDidUpdate react-three-renderer.
  5. это вызывает метод _render.
  6. метод _render обновляет холст, передавая реквизит.дети (объекты сетки) в библиотеку Three.
  7. при монтировании объектов сетки вызываются соответствующие ссылки.

это объясняет, что вы наблюдение в консольных операторах.