تفکر به سبک React Three Fiber: تغییر ذهنیتی که هر توسعه‌دهنده Three.js به آن نیاز دارد

اگر با Three.js راحت هستید اما React Three Fiber (R3F) هنوز کمی… جادویی به نظرتان می‌رسد، تنها نیستید.

وقتی برای اولین بار کد React Three Fiber را خواندم، فوراً حس کردم چیزی سر جایش نیست. مدام از خودم می‌پرسیدم: «پس `scene.add()` کجاست؟» کدی مثل این می‌دیدم:

<mesh position={[1, 2, 3]}>
  <boxGeometry />
  <meshStandardMaterial color="orange" />
</mesh>

و ذهن Three.jsی من نمی‌توانست آن را بفهمد. چطور یک تگ ساده JSX یک آبجکت واقعی Three.js می‌سازد؟ رندرر کجاست؟ صحنه کجاست؟ چه کسی مش را می‌سازد؟

جواب خیلی ساده‌تر از انتظارم بود: React Three Fiber جای Three.js را نمی‌گیرد؛ شیوه فکر کردن شما درباره ساخت صحنه را تغییر می‌دهد. وقتی این را فهمیدم، همه چیز جا افتاد.

React Three Fiber یک موتور سه‌بعدی جدید نیست

یکی از بزرگ‌ترین سوءبرداشت‌ها این است که R3F جایگزین Three.js است. نیست. React Three Fiber یک رندرر سفارشی React است که روی Three.js ساخته شده. موتور رندر را عوض نمی‌کند — شیوه تعامل شما با آن را عوض می‌کند.

هر چیزی که در R3F رندر می‌کنید در نهایت یک آبجکت واقعی Three.js می‌شود. <mesh /> معادل new THREE.Mesh(...) است و <boxGeometry /> می‌شود new THREE.BoxGeometry(...).

موتور رندر، دوربین‌ها، متریال‌ها و Scene Graph همچنان Three.js هستند. فقط مدل برنامه‌نویسی تغییر می‌کند.

Three.js: شما صحنه را کنترل می‌کنید

در Three.js خالص، صحنه منبع حقیقت است:

const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cube.position.set(1, 2, 3);
scene.remove(cube);

آبجکت‌ها را می‌سازید، اضافه می‌کنید، به‌روز می‌کنید و حذف می‌کنید — همه با تغییر مستقیم صحنه. اگر کسی بپرسد «الان چه آبجکت‌هایی وجود دارند؟»، scene.children را بررسی می‌کنید.

React Three Fiber: شما صحنه را توصیف می‌کنید

R3F فلسفه کاملاً متفاوتی دارد. به جای اینکه به موتور بگویید صحنه را چطور بسازد، توصیف می‌کنید صحنه چه شکلی باید باشد:

<>
  <Cube />
  <Light />
  <Floor />
</>

این HTML نیست؛ این Scene Graph شماست. به جای «این مش را اضافه کن» می‌گویید «اینجا باید یک مش وجود داشته باشد» — و بقیه کار با React است.

<Canvas> واقعاً چه کار می‌کند؟

راحت است فکر کنیم <Canvas> فقط یک تگ HTML <canvas> است. اما نیست. React Three Fiber پشت صحنه این‌ها را می‌سازد و مدیریت می‌کند:

  • یک THREE.Scene
  • یک THREE.WebGLRenderer
  • یک دوربین
  • حلقه رندر
  • مدیریت تغییر اندازه
  • رویدادهای اشاره‌گر
  • چرخه عمر آبجکت‌ها
  • Reconciliation در React

به همین دلیل دیگر به ندرت لازم است این‌ها را دستی بسازید.

بزرگ‌ترین تغییر ذهنیت

در Three.js صحنه را تغییر می‌دهید. در R3F، state را تغییر می‌دهید. کلیک یک دکمه را در نظر بگیرید.

Three.js:

button.onclick = () => {
  scene.add(cube);
};

React Three Fiber:

const [showCube, setShowCube] = useState(false);

<button onClick={() => setShowCube(true)} />
{showCube && <Cube />}

نه scene.add() و نه ساختن دستی آبجکت. state تغییر می‌کند، React دوباره رندر می‌کند و صحنه خودکار به‌روز می‌شود.

JSX دیگر فقط HTML نیست

JSXهای تودرتو آبجکت‌های تودرتوی Three.js می‌سازند:

<group>
  <mesh>
    <pointLight />
  </mesh>
</group>

معادل است با:

Scene
└── Group
    └── Mesh
        └── PointLight

فرزندها همان فراخوانی‌های add() هستند. وقتی JSX را Scene Graph ببینید نه HTML، فهم R3F خیلی ساده‌تر می‌شود.

کامپوننت‌ها آبجکت‌های سه‌بعدی را نمایش می‌دهند

در اپ‌های React، کامپوننت‌ها معمولاً بخش‌هایی از رابط کاربری‌اند: <Navbar />، <Sidebar />، <Footer />. در R3F کامپوننت‌ها اغلب آبجکت‌های دنیای شما هستند: <Player />، <Car />، <Tree />، <Enemy />. هر کامپوننت آبجکت‌های Three.js خودش را دارد و این کار سازمان‌دهی صحنه‌های پیچیده را خیلی آسان‌تر می‌کند.

رندر دوباره یعنی ساختن دوباره نیست

خیلی‌ها فکر می‌کنند هر رندر React آبجکت‌های جدید Three.js می‌سازد. این‌طور نیست. اگر فقط موقعیت عوض شود:

<mesh position={[5, 0, 0]} />

R3F مش را از بین نمی‌برد و دوباره نمی‌سازد؛ فقط mesh.position.set(5, 0, 0) را به‌روز می‌کند — همان‌طور که React به جای ساختن دوباره کل صفحه، DOM را به‌روز می‌کند.

هوک‌هایی که مهم‌اند

بهتر است هوک‌های R3F را مفاهیم آشنای Three.js ببینید:

  • useGLTF() — نسخه React از GLTFLoader.load()
  • useFrame() — نسخه React از requestAnimationFrame()
  • useThree() — دسترسی به صحنه، دوربین، رندرر و آبجکت‌هایی که <Canvas> ساخته
  • useLoader() — لودر عمومی دارایی‌ها
  • useRef() — ارجاع به آبجکت واقعی Three.js

این هوک‌ها جای Three.js را نمی‌گیرند؛ آن را به شکلی سازگار با React در اختیارتان می‌گذارند.

useFrame() حلقه اصلی شما نیست

عادت رایج از Three.js این است که همه چیز را داخل حلقه رندر بگذاریم. در R3F، useFrame() فقط برای چیزهایی است که در هر فریم تغییر می‌کنند:

useFrame(() => {
  meshRef.current.rotation.y += 0.01;
});

انیمیشن؟ ✅ بله. فیزیک؟ ✅ بله. ساختن آبجکت؟ ❌ معمولاً نه.

صحنه دیگر منبع حقیقت نیست

احتمالاً مهم‌ترین مفهوم همین است. در Three.js، state داخل صحنه است. در R3F، state داخل React است و صحنه فقط نتیجه بصری آن است. به همین دلیل خیلی از پروژه‌های R3F از Zustand، Redux یا state خود React استفاده می‌کنند: داده‌های شما صحنه را می‌سازند، نه برعکس.

یک اشتباه رایج

تازه‌کارهای R3F اغلب این‌طور می‌نویسند:

useEffect(() => {
  scene.add(mesh);
}, []);

گاهی این کاملاً درست است. اما اگر زیاد این کار را می‌کنید، از خودتان بپرسید: «آیا می‌شود این را با JSX بیان کرد؟» اغلب جواب مثبت است. R3F وقتی بهترین کارایی را دارد که React مسئول چرخه عمر آبجکت‌ها باشد.

یک راه ساده برای فکر کردن

هر وقت دیدید از خودتان می‌پرسید «`scene.add()` را کجا صدا بزنم؟»، سؤال دیگری بپرسید: «چه state ای باید تغییر کند تا React این آبجکت را رندر کند؟»

جمع‌بندی

Three.js می‌پرسد: «چطور صحنه را تغییر دهم؟» React Three Fiber می‌پرسد: «صحنه باید چه شکلی باشد؟»

همان موتور رندر، همان WebGL، همان دوربین‌ها، متریال‌ها و آبجکت‌ها. تنها چیزی که عوض می‌شود شیوه بیان آن‌هاست. اگر از Three.js می‌آیید، هر چیزی که درباره دوربین، نور، متریال، لودر، شیدر و Scene Graph می‌دانید را نگه دارید — همه آن مفاهیم دقیقاً همان‌اند. React Three Fiber فقط اجازه می‌دهد React چرخه عمر آن‌ها را هماهنگ کند.

وقتی این تغییر ذهنیت اتفاق بیفتد، JSX دیگر جادو به نظر نمی‌رسد و به یکی از طبیعی‌ترین راه‌ها برای ساخت اپلیکیشن‌های سه‌بعدی تعاملی در وب تبدیل می‌شود.