التفكير بأسلوب 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(...).

محرك العرض والكاميرات والخامات وشجرة المشهد كلها ما زالت 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؛ هذه شجرة مشهدك. بدل «أضف هذه الشبكة» تقول «يجب أن توجد شبكة هنا» — ويتولى React الباقي.

ماذا يفعل <Canvas> فعلًا؟

من السهل الظن أن <Canvas> مجرد عنصر HTML <canvas>. لكنه ليس كذلك. خلف الكواليس ينشئ React Three Fiber ويدير:

  • THREE.Scene
  • THREE.WebGLRenderer
  • كاميرا
  • حلقة العرض
  • التعامل مع تغيير الحجم
  • أحداث المؤشر
  • دورة حياة الكائنات
  • مطابقة React (Reconciliation)

لذلك نادرًا ما تحتاج إلى إنشاء هذه الكائنات يدويًا.

التحول الذهني الأكبر

في 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() ولا إنشاء يدوي للكائنات. تتغير الحالة، يعيد React العرض، ويتحدّث المشهد تلقائيًا.

JSX لم يعد مجرد HTML

ينشئ JSX المتداخل كائنات Three.js متداخلة:

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

يعادل:

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

العناصر الأبناء هي ببساطة استدعاءات add(). عندما ترى JSX كشجرة مشهد لا كـ 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 تعيش الحالة داخل المشهد. في R3F تعيش داخل React، والمشهد مجرد النتيجة المرئية. لهذا تستخدم مشاريع R3F كثيرة Zustand أو Redux أو حالة React: بياناتك تقود المشهد، لا العكس.

خطأ شائع

يكتب المبتدئون في R3F غالبًا:

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

أحيانًا يكون هذا صحيحًا تمامًا. لكن إن كنت تفعله كثيرًا، اسأل نفسك: «هل يمكن التعبير عن هذا بـ JSX؟» غالبًا الجواب نعم. يعمل R3F بأفضل شكل عندما يكون React مسؤولًا عن دورة حياة الكائنات.

طريقة بسيطة للتفكير

كلما وجدت نفسك تسأل «أين أستدعي `scene.add()`؟»، اطرح سؤالًا آخر: «ما الحالة التي يجب أن تتغير ليعرض React هذا الكائن؟»

الخلاصة

يسأل Three.js: «كيف أغيّر المشهد؟» ويسأل React Three Fiber: «كيف يجب أن يبدو المشهد؟»

المحرك نفسه، وWebGL نفسه، والكاميرات والخامات والكائنات نفسها. الشيء الوحيد الذي يتغير هو طريقة التعبير عنها. إن كنت قادمًا من Three.js فاحتفظ بكل ما تعرفه عن الكاميرات والإضاءة والخامات والمحمّلات والشيدرات وشجرة المشهد — كلها كما هي. يتيح React Three Fiber فقط لـ React تنسيق دورة حياتها.

وعندما يحدث هذا التحول الذهني، يتوقف JSX عن الظهور كأنه سحر — ويصبح من أكثر الطرق طبيعية لبناء تطبيقات ثلاثية الأبعاد تفاعلية على الويب.