How do you stop the camera position from getting reset, when the component state gets updated?

Hey folks, I am new to threejs and trying to hack something. I am struggling to find a working solution since yesterday.
I am attaching a simple code, where I have placed a point on a X-Y-Z plane.
I want to move the point using the keyboard and I want to move the orbits using mouse.
But whenever point moves or the move mode gets activated, the camera position gets reset to the initial start position.
I want to maintain the same camera position and then move the point, how do i accomplish it?

import React, { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

const PlanePoint = () => {
  const mountRef = useRef(null);
  const controlsRef = useRef(null);
  const [moveMode, setMoveMode] = useState(false);
  const [pointPosition, setPointPosition] = useState({ x: 2, y: 3, z: 5 });

  useEffect(() => {
    const width = 500;
    const height = 500;

    // Scene
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0xeeeeee);

    // Camera
    const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
    camera.position.set(0, 0, 10);

    // Renderer
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(width, height);
    mountRef.current.appendChild(renderer.domElement);

    // Orbit Controls
    const controls = new OrbitControls(camera, renderer.domElement);
    controlsRef.current = controls;

    // Point
    const pointGeometry = new THREE.SphereGeometry(0.1, 32, 32);
    const pointMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
    const point = new THREE.Mesh(pointGeometry, pointMaterial);
    point.position.set(pointPosition.x, pointPosition.y, pointPosition.z);
    scene.add(point);

    // Axes Helper
    const axesHelper = new THREE.AxesHelper(5);
    scene.add(axesHelper);

    // Grid Helpers
    const gridHelperXY = new THREE.GridHelper(10, 10);
    gridHelperXY.rotation.x = Math.PI / 2; // Rotate to lie on the x-y plane
    scene.add(gridHelperXY);

    const gridHelperXZ = new THREE.GridHelper(10, 10);
    scene.add(gridHelperXZ);

    // Animation function
    const animate = function () {
      requestAnimationFrame(animate);

      if (!moveMode) {
        controls.update();
      }

      point.position.set(pointPosition.x, pointPosition.y, pointPosition.z);
      renderer.render(scene, camera);
    };

    animate();

    const handleKeyDown = (event) => {
      if (moveMode) {
        switch (event.key) {
          case 'ArrowUp':
            setPointPosition((prev) => ({ ...prev, y: prev.y + 0.1 }));
            break;
          case 'ArrowDown':
            setPointPosition((prev) => ({ ...prev, y: prev.y - 0.1 }));
            break;
          case 'ArrowLeft':
            setPointPosition((prev) => ({ ...prev, x: prev.x - 0.1 }));
            break;
          case 'ArrowRight':
            setPointPosition((prev) => ({ ...prev, x: prev.x + 0.1 }));
            break;
          case 'w':
            setPointPosition((prev) => ({ ...prev, z: prev.z + 0.1 }));
            break;
          case 's':
            setPointPosition((prev) => ({ ...prev, z: prev.z - 0.1 }));
            break;
          default:
            break;
        }
      }
    };

    window.addEventListener('keydown', handleKeyDown);

    return () => {
      mountRef.current.removeChild(renderer.domElement);
      window.removeEventListener('keydown', handleKeyDown);
    };
  }, [moveMode, pointPosition]);

  const toggleMoveMode = () => {
    setMoveMode(!moveMode);
    if (controlsRef.current) {
      controlsRef.current.enabled = !moveMode;
    }
  };

  return (
    <div>
      <div ref={mountRef} style={{ width: '500px', height: '500px' }}></div>
      <div>
        <button
          onClick={toggleMoveMode}
          style={{
            backgroundColor: moveMode ? 'lightblue' : 'lightgray',
            padding: '10px',
            border: 'none',
            cursor: 'pointer',
          }}
        >
          {moveMode ? 'Deactivate Move' : 'Activate Move'}
        </button>
      </div>
    </div>
  );
};

export default PlanePoint;

Take a look on this example:
https://threejs.org/examples/?q=Controls#misc_controls_pointerlock

this example doesn’t update any state, it’s just controlling the orbit (camera view).
I want to move an object and retain the camera view.

This is the example I was looking for. Got it!
https://threejs.org/examples/?q=controls#misc_controls_transform

1 Like

If you’re using react with three - consider react-three-fiber instead of mixing imperative vanilla with react components. It will make your life way easier and you’ll also gain access to drei - which gives you a ton of stuff out of the box.

I was thinking about it and searching for information regarding the relationship between useEffect and useLayoutEffect, and how they affect rendering and re-rendering of the scene and camera.

Can you refer me to some good examples I can look upto? Or any blog or documentation?

Some projects to study:

@iHast @mjurczyk I tried implementing this misc_controls_transform example
using react-three-fiber.
Here is the sandbox link to it => Sandbox link using react-three-fiber

Both are poles apart, the orbit controls move even when the object axis is selected to move, what am i doing wrong here?

@iHast @mjurczyk updated link => https://codesandbox.io/p/sandbox/threejs-example-react-47rkf4

sorry, i got the wrong link.