reactjs 在使用canvas API时,如何在React中构造状态?

beq87vna  于 2023-11-18  发布在  React
关注(0)|答案(3)|浏览(101)

bounty已结束,回答此问题可获得+50声望奖励,奖励宽限期20小时后结束,lukas23希望引起更多关注此问题。

我正在使用canvas API,其中我有许多可以显示的矩形。这些矩形彼此连接,并且可以在canvas上拖动(移动)。这个canvas基本上是用户数据层的可视化表示。
为了找到一种最好的方法来构造状态,它将保存所有矩形的信息,我在Recoil状态中构造了状态,因为这是我们正在使用的状态库。Recoil state使得在多个组件之间同步数据变得很容易,但我注意到canvas变得非常慢,因为每次我移动一个矩形,我必须用map方法更新整个状态,因此每次移动一英寸都要在内存中创建新对象。
然后我尝试了这个技巧,其中画布矩形的数据和它们之间的连接用classes表示,我在类中创建了方法来更新对象,它们直接变异,我只是发送新数组来React状态,但对象发生了变异。这加快了画布交互的速度,但是我觉得不太好,因为我没有真正用react的方式来做,而且通过对象上的方法更新对象而不对对象进行浅拷贝可能会导致许多潜在的问题。Object.freeeze()在状态数组中的每个对象上。
我现在面临的问题是有太多的 prop 传递,因为我在本地state中保存画布的状态。我试图找到最好的方法来结构状态,同时考虑到React性质和许多潜在的footgun的所有性能影响。
我的数据看起来有点像这样。

const stateArray = [
  firstTask: {
     rectangles: [],
     connections: [],
     id: 1,
     name: '',
     etc..
   },
   secondTask: {
     rectangles: [],
     connections: [],
     id: 1,
     name: '',
     etc..
   }
]

字符串
我已经为矩形的每个对象创建了一个类,其中存储了关于它们的尺寸和坐标的所有信息。
例如,更改坐标的代码如下所示:

setRectangles(prev => {
  return prev.map(rect => {
   if (rect.id === id) {
     rect.updateCoordinates(x, y)
   }
   return rect; 
})
}


正如你所看到的,我正在通过对象方法对对象进行直接变化。这是目前为止性能最好的方法,但从React不变性方法来看,它看起来并不好。
如果任何人有任何类似的问题,或有任何想法,我如何才能更好地结构,这将是非常感谢!

ercv8c1e

ercv8c1e1#

欢迎来到应用状态和短暂状态之间的边界。
暂时状态对组件很重要,但对该组件的消费者在语义上并不重要。例如,一个WebOnChange事件对应用程序来说是非常重要的状态。但是,它是打开状态,或者是焦点状态不是。
所以让我们以一个矩形为例,它可以被拖到3个不同的区域,“Backlog”,“InProgress”和“Done”。
每个区域对应用程序的其他部分都有语义意义。例如,我想更新每个区域中的矩形计数。
当我点击rect并开始拖动的时候--状态会分裂,现有的react可能会停留在原来的位置--但是它会得到一个 * 短暂的 * 拖动的孪生体。在任何时候(onDragOver),应用都不关心它的rect在哪里。只有当它做了一些语义上的事情,比如被放入一个区域时--应用状态才需要更新。
值得注意的是,短暂的状态是存在的--它只是不需要连接到你慢得多的应用程序状态逻辑中。
所以你要设置所有的东西在短暂的状态下拖放(或者其他什么)(这是很快的),然后更新状态,只有当它很重要的时候。
例如,here示例,您将仅在放置时创建状态

<div>
  <p id="source" draggable="true">
    Select this element, drag it to the drop zone and then release the selection
    to move the element.
  </p>
</div>
<div id="target">Drop Zone</div>

<button id="reset">Reset example</button>

个字符
同样的原则也适用于canvas --让canvas来处理它短暂的状态。但在关键点上,要更新应用程序的状态。

tyky79it

tyky79it2#

据我所知,没有好的库可以直接在canvas上使用类似react的方法(带组件和钩子)。
但是three.js库有一个非常好的react Package 器,它在引擎盖下使用了canvas。有了这个,你就可以在useFrame这样的实用钩子的帮助下将功能封装到组件中。但是请记住,尽管这个库提供了很多功能,使你能够使用3D模型,甚至创建游戏,但它非常繁重。如果它不适合,你需要测试它。对你的案子很有效

w1jd8yoj

w1jd8yoj3#

在使用canvas API时,由于频繁更新canvas元素的性能影响,在React中构造状态可能具有挑战性。但是,您可以采取一些方法来平衡性能和不变性。
1.使用Context API Context API提供了一种在组件之间共享状态的方法,而无需通过组件树传递props。这对于管理与画布相关的状态非常有用,例如矩形列表及其坐标。

const CanvasContext = React.createContext();

const CanvasProvider = ({ children }) => {
  const [rectangles, setRectangles] = React.useState([]);

  const handleUpdateRectangle = (id, x, y) => {
    const updatedRectangles = rectangles.map(rectangle => {
      if (rectangle.id === id) {
        return { ...rectangle, x, y };
      }
      return rectangle;
    });
    setRectangles(updatedRectangles);
  };

  return (
    <CanvasContext.Provider value={{ rectangles, handleUpdateRectangle }}>
      {children}
    </CanvasContext.Provider>
  );
};

const Canvas = () => {
  const { rectangles, handleUpdateRectangle } = React.useContext(CanvasContext);

  // Use rectangles and handleUpdateRectangle to render and interact with the canvas
};

字符串
1.使用状态管理库Redux或MobX等状态管理库可以帮助管理复杂的状态,并提供用于不变性和性能优化的工具。当处理大量数据或需要在多个组件之间共享状态时,这些库特别有用。

const store = createStore(reducer);
    
    const Canvas = () => {
      const rectangles = useSelector(state => state.rectangles);
      const dispatch = useDispatch();
    
      const handleUpdateRectangle = (id, x, y) => {
        dispatch({ type: 'UPDATE_RECTANGLE', id, x, y });
      };
    
      // Use rectangles and handleUpdateRectangle to render and interact with the canvas
    };


1.优化状态更新无论您选择哪种状态管理方法,优化状态更新以避免不必要的重新渲染都很重要。这可以通过使用对象的浅层副本或使用像useMemo这样的记忆化技术来避免重新计算昂贵的函数来完成。

const updateRectangle = React.useCallback((id, x, y) => {
      const updatedRectangle = { ...rectangle, x, y };
      setRectangles(prevRectangles => prevRectangles.map(rectangle => {
        if (rectangle.id === id) {
          return updatedRectangle;
        }
        return rectangle;
      }));
    }, [rectangle]);


1.使用自定义钩子自定义钩子可以提供一种可重用的方式来管理状态和处理与画布相关的交互。这可以帮助封装逻辑并使组件专注于其特定的职责。

const useCanvasState = () => {
      const [rectangles, setRectangles] = React.useState([]);
    
      const handleUpdateRectangle = (id, x, y) => {
        const updatedRectangles = rectangles.map(rectangle => {
          if (rectangle.id === id) {
            return { ...rectangle, x, y };
          }
          return rectangle;
        });
        setRectangles(updatedRectangles);
      };
    
      return { rectangles, handleUpdateRectangle };
    };

    const Canvas = () => {
      const { rectangles, handleUpdateRectangle } = useCanvasState();
    
      // Use rectangles and handleUpdateRectangle to render and interact with the canvas
    };


通过考虑这些方法并优化状态更新,您可以在使用canvas API的同时有效地管理React中的状态,从而确保性能和可维护性。

相关问题