هنگام ساخت اپلیکیشنهای سهبعدی تعاملی با React و Three.js، هوکهای React نقش پل بین منطق رابط کاربری و موتور رندر را دارند. این مقاله هوکها را در سه لایه توضیح میدهد:
- مفهوم کلی هوکهای React
- استفاده واقعی در یک پروژه Three.js، قدم به قدم
- کدام هوکها در اپهای واقعی بیشترین اهمیت را دارند
🧠 ۱. مرور سریع هوکهای React
هوکها به کامپوننتهای تابعی کمک میکنند اینها را مدیریت کنند:
- 🟢 State — دادهای که رابط کاربری را تغییر میدهد
- 🔵 Side effect — عملیات بیرونی
- 🟣 Memoization — بهینهسازی پرفورمنس
🟢 useState ← state رابط کاربری. برای مقادیری که تغییر میکنند و روی رندر اثر دارند: آبجکت انتخابشده، رنگ انتخابشده، کنترلهای حرکت.
🔵 useEffect ← اثرات جانبی. برای عملیات بیرون از رندر React: راهاندازی صحنه Three.js، اتصال رویدادها، همگامسازی React ← Three.js.
🟣 useRef ← ذخیرهسازی ماندگار. برای نگه داشتن عناصر DOM، صحنه، دوربین و رندرر Three.js و ارجاع به مشها — بدون ایجاد رندر دوباره.
🟡 useMemo ← بهینهسازی پرفورمنس. از محاسبات پرهزینه تکراری جلوگیری میکند و هندسه یا مقادیر مشتقشده را کش میکند.
🔴 useCallback ← توابع پایدار. از ساخته شدن دوباره توابع جلوگیری میکند؛ برای Event Listenerهایی مثل resize و click مفید است.
🎮 ۲. هوکهای React در یک پروژه واقعی Three.js
حالا همه چیز را روی یک سیستم سهبعدی واقعی، قدم به قدم پیاده کنیم.
قدم ۱: useRef ← لایه موتور Three.js
آبجکتهای اصلی Three.js را بیرون از چرخه رندر React نگه میداریم:
const sceneRef = useRef<THREE.Scene | null>(null);
const cameraRef = useRef<THREE.PerspectiveCamera | null>(null);
const rendererRef = useRef<THREE.WebGLRenderer | null>(null);
const cubesRef = useRef<THREE.Mesh[]>([]);چرا؟ چون این آبجکتها state رابط کاربری نیستند، نباید رندر دوباره ایجاد کنند و باید بین رندرها باقی بمانند.
قدم ۲: useState ← لایه رابط کاربری
const [selectedProduct, setSelectedProduct] = useState<Product | null>(null);
const [selectedColor, setSelectedColor] = useState("#ff9900");
const [moveX, setMoveX] = useState(0);اینها رابط کاربری را بهروز میکنند، تعامل را کنترل میکنند و افکتها را فعال میکنند.
قدم ۳: useMemo ← بهینهسازی هندسه
ثابت (یک بار ساخته میشود) — هندسه پرهزینه است، پس فقط یک بار ساخته میشود:
const baseGeometry = useMemo(() => {
return new THREE.BoxGeometry(1, 1, 1);
}, []);پویا (وابسته به state) — مقداری که از state به دست میآید:
const dynamicOffset = useMemo(() => {
return moveX * 0.5;
}, [moveX]);قدم ۴: useCallback ← توابع پایدار
هندلر تغییر اندازه (بدون وابستگی):
const handleResize = useCallback(() => {
const camera = cameraRef.current;
const renderer = rendererRef.current;
if (!camera || !renderer || !mountRef.current) return;
camera.aspect = mountRef.current.clientWidth / mountRef.current.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(mountRef.current.clientWidth, mountRef.current.clientHeight);
}, []);هندلر کلیک (وابسته به state) — رفتار کلیک به رنگ فعلی بستگی دارد:
const handleCubeClick = useCallback(
(mesh: THREE.Mesh) => {
const mat = mesh.material as THREE.MeshStandardMaterial;
mat.color.set(selectedColor);
setSelectedProduct(mesh.userData as Product);
},
[selectedColor]
);قدم ۵: useEffect ← راهاندازی موتور
useEffect(() => {
if (!mountRef.current) return;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(/* ... */);
const renderer = new THREE.WebGLRenderer();
mountRef.current.appendChild(renderer.domElement);
}, []);فقط یک بار اجرا میشود و کل موتور سهبعدی را راه میاندازد: صحنه، دوربین، رندرر، نورها و مکعبها.
قدم ۶: سیستم تعامل (Raycaster)
const onMouseClick = (event: MouseEvent) => {
raycaster.setFromCamera(mouse, camera);
const hits = raycaster.intersectObjects(cubesRef.current);
if (hits.length > 0) {
handleCubeClick(hits[0].object as THREE.Mesh);
}
};با تبدیل موقعیت دوبعدی ماوس به یک اشعه سهبعدی، مکعب کلیکشده را تشخیص میدهد.
قدم ۷: حلقه انیمیشن
const animate = () => {
requestAnimationFrame(animate);
cubesRef.current.forEach((cube) => {
cube.rotation.y += 0.01;
cube.position.x += dynamicOffset * 0.001;
});
renderer.render(scene, camera);
};در هر فریم اجرا میشود، دنیای سهبعدی را بهروز میکند و صحنه را پیوسته رندر میکند.
قدم ۸: بهروزرسانیهای واکنشی (useEffect)
همگامسازی رنگ:
useEffect(() => {
if (selectedMeshRef.current) {
(selectedMeshRef.current.material as THREE.MeshStandardMaterial).color.set(selectedColor);
}
}, [selectedColor]);بهروزرسانی موقعیت:
useEffect(() => {
cubesRef.current.forEach((cube, i) => {
cube.position.x = i * 2 - 4 + moveX;
});
}, [moveX]);🚀 ۳. این پروژه چه چیزی یاد میدهد
- React = کنترلکننده رابط کاربری
- Three.js = موتور رندر
- هوکها = لایه پل
⚡ ۴. کدام هوکها در پروژههای واقعی مهمترند؟
🔥 مهمترین: useRef (هسته موتور، حیاتی)، useEffect (مدیریت چرخه عمر، حیاتی)، useState (کنترل رابط کاربری، مهم).
🟡 ثانویه: useMemo (بهینهسازی پرفورمنس) و useCallback (پایداری رویدادها).
📌 نکته نهایی
در معماری React + Three.js، React state را مدیریت میکند، Three.js رندر را انجام میدهد و هوکها این دو دنیا را به شکلی کارآمد به هم وصل میکنند.
کد کامل: react-learning-journey در GitHub — کامپوننت Canvas3D.tsx را ببینید.



