首页 > 解决方案 > 在 React js 中只加载一次组件

问题描述

所以我有一个 React 应用程序它有 2 个页面让我们说第一个是主页,第二个是我的投资组合部分,我的投资组合部分有 2 个功能组件

主页.js

import React from 'react'

const Home = () => {
  return (
    <h1>Home Screen</h1>
  )
}

export default Home

投资组合部分

import React from 'react'
import Projects from '../components/Projects'
import Testimonials from '../components/Testimonials'
const Portfolio = () => {
  return (
    <>
    <h1>Portfolio</h1>
    <Projects/>
    <Testimonials/>
    </>
  )
}

export default Portfolio 

当我访问 Home 时,Portfolio 一切正常,但是当我再次访问它们时,它们每次都重新加载

我不希望投资组合部分中的项目组件加载不止一次我希望它在启动时/启动站点时仅加载一次,甚至在访问投资组合部分(即在后台)之前,因为它有大文件将需要时间来加载并保持不变,直到用户自愿刷新页面。

该组件中有一些使用three.js的3d模型所以它不是一个静态站点它将有自己的动画但是每次访问页面时加载整个组件会花费很多时间

但是推荐组件会动态变化

因此,在 2 个组件中,我希望一个保持不变而无需重新加载,而另一个则动态更改

我怎样才能只加载一次组件任何建议都会帮助我我看到了 UseMemo 但我不知道这是否会对我有帮助

标签: javascriptreactjsthree.jsreact-routerreact-hooks

解决方案


首次渲染相关组件 ( Projects) 时,创建动画并将其缓存在组件外部的变量中(let animation在示例中)。每当安装组件时,从外部变量中获取动画,然后启动它。当它被卸载时停止动画。

注意:如果您只渲染组件的单个实例,这将起作用。如果你要渲染多个实例,你需要想出一个系统来缓存每个实例的重新渲染动画 - 可能是某种 Map ,每个实例都有一个常量 id。

示例- 切换页面,动画将继续

const { Fragment, useState, useEffect, useRef } = React

let animation

const Projects = () => {
  const container = useRef();
  
  useEffect(() => {    
    if(!animation) {
      animation = createAnimation()
    }
    
    container.current.appendChild(animation.domElement)
    
    animation.start()
    
    return () => animation.stop()
  }, [])

  return (
    <div ref={container} /> 
  )
}

const Home = () => (
  <span>Home Screen</span>
)

const Portfolio = () => (
  <Fragment>
    <span>Portfolio</span>
    <Projects />
  </Fragment>
)

const App = () => {
  const [showPortfolio, setShowPortfolio] = useState(false)

  return (
    <div>
      <button 
        onClick={() => setShowPortfolio(!showPortfolio)}>
        Toggle
      </button>
      
      {showPortfolio ? <Portfolio /> : <Home />}
    </div>
  )
}

ReactDOM.render(
  <App />,
  root
)

function createAnimation() {
  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(20, window.innerWidth / window.innerHeight, 1, 1000);

  const renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth / 2, window.innerHeight / 2);

  const geometry = new THREE.BoxGeometry();
  const material = new THREE.MeshBasicMaterial({
    color: 0x00ff00
  });
  const cube = new THREE.Mesh(geometry, material);
  scene.add(cube);

  camera.position.z = 5;

  let requestId;

  const animate = () => {
    requestId = requestAnimationFrame(animate);

    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;

    renderer.render(scene, camera);
  };

  return {
    start: animate,
    stop: () => cancelAnimationFrame(requestId),
    domElement: renderer.domElement
  }
}
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r126/three.min.js" integrity="sha512-n8IpKWzDnBOcBhRlHirMZOUvEq2bLRMuJGjuVqbzUJwtTsgwOgK5aS0c1JA647XWYfqvXve8k3PtZdzpipFjgg==" crossorigin="anonymous"></script>

<div id="root"></div>


推荐阅读