我可以做些啥来存储类的实例并在反应应用程序的上下文中管理状态

Posted

技术标签:

【中文标题】我可以做些啥来存储类的实例并在反应应用程序的上下文中管理状态【英文标题】:What can I do to store instances of a class and manage state in the context of a react app我可以做些什么来存储类的实例并在反应应用程序的上下文中管理状态 【发布时间】:2019-12-03 00:35:42 【问题描述】:

我正在使用这个 Rectangle 类

class Rectangle 
  constructor(x, y, width, height, color, hasCollision = false, kills = false) 
    this.A = new Point(x, y)
    this.B = new Point(x + width, y)
    this.C = new Point(x + width, y + height)
    this.D = new Point(x, y + height)
    this.center = new Point(x + width / 2, y + height / 2)
    this.width = width
    this.height = height
    this.color = color
    this.hasCollision = hasCollision
    this.kills = kills
  

  get vertices() 
    const  A, B, C, D  = this
    return 
      A,
      B,
      C,
      D
    
  

  get x() 
    return this.A.x
  

  get y() 
    return this.A.y
  

  static translate(rectangle, vector) 
    for (let vertice of Object.values(rectangle.vertices)) 
      vertice.translate(vector)
    
    rectangle.center.translate(vector)
  

  translate(vector) 
    Rectangle.translate(this, vector)
  

  hasUserFallenInTrap(user) 
    if (circleIntersectsRectangle(user, this) && this.kills) 
      return true
    

    return false
  

  display(ctx, useOwnColor = true) 
    const  x, y, width, height  = this
    if (useOwnColor) 
      ctx.fillStyle = this.color
        ? this.color.hexString
        : this.kills
        ? 'red'
        : '#000000'
    
    ctx.fillRect(x, y, width, height)
  

我需要将一堆 Rectangle 存储在一个数组中,以便我可以在画布中显示它们(包装在 React 组件中)。组件不会更新每一帧,我在画布上使用自己的绘制函数:

// Here is the component
class Game extends React.Component 
  constructor(props) 
    super(props)
    const world = loadMap('world1')
    this.state = 
      currentWorld: world,
      users: ,
      user: new User(
        world.spawn.center.x,
        world.spawn.center.y,
        12,
        Color.random(),
        '',
        world.spawn
      )
    
    this.canvas = React.createRef()
    this.ctx = null
  

  componentDidMount() 
    const  user  = this.state
    server.userConnects(user)
    openConnection()
    this.ctx = this.canvas.current.getContext('2d')
    setPointerLock(this.canvas.current, this.mouseMoved)
    this.request = window.requestAnimationFrame(this.draw)
  

  componentWillUnmount() 
    closeConnection()
    window.cancelAnimationFrame(this.request)
  

  shouldComponentUpdate() 
    return false
  

  updateUserID = id => 
    this.setState( u )
  

  mouseMoved = event => 
    const  currentWorld, user  = this.state
    const displacement = new Vector(
      event.movementX / pixelRatio,
      event.movementY / pixelRatio
    )
    user.translate(displacement)
    resolveWorldBordersCircleCollision(user)
    for (const w of currentWorld.walls) 
      if (w.hasCollision) stepCollisionResolve(user, w)
    
    server.updateUserPosition(user)
  

  draw = () => 
    const  currentWorld, users, user  = this.state
    this.request = window.requestAnimationFrame(this.draw)
    this.ctx.clearRect(0, 0, WIDTH, HEIGHT)
    this.ctx.fillText(fpsCounter.fps, 1000, 20)
    currentWorld.walls.forEach(w => 
      if (w.hasCollision && resolveCollisionCircleRectangle(user, w)) 
        server.updateUserPosition(user)
      
      w.display(this.ctx)
    )
    currentWorld.movableWalls.forEach(w => 
      w.walkPath()
      if (w.hasCollision && resolveCollisionCircleRectangle(user, w)) 
        server.updateUserPosition(user)
      
      w.display(this.ctx)
    )
    currentWorld.traps.forEach(t => 
      t.display(this.ctx)
      if (t.hasUserFallenInTrap(user)) 
        user.kill()
        server.updateUserPosition(user)
      
    )
    user.display(this.ctx, false)
    Object.values(users)
      .filter(u => u.id !== user.id)
      .forEach(u => 
        u.display(this.ctx, true)
      )
  

  render() 
    return (
      <>
        <canvas ref=this.canvas id="canvas" width=WIDTH height=HEIGHT />
      </>
    )
  

我不确定如何存储和管理这个矩形数组。

我正在使用类方法 translate 平移一个矩形。

const rect = new Rectangle(10, 10, 10, 10)
rect.translate(x: 10, y: 20)

但如果矩形处于组件状态,我就不能这样做。 调用rect.translate 会直接改变状态。

每次我更新状态时创建一个新对象完全违背了使用此类的目的。

使用对象解构会创建一个普通的新对象,因此我将无法再调用它的display 方法:

// changing a single rectangle for example 
const  rectangles  = this.state
this.setState( rectangles: [...rectangles.splice(0,index) ,  ...rectangles[index], x: rectangles[index].x + 10, y: rectangles[index].y + 10 , ...rectangles.splice(index + 1)]

在任何 React 组件之外使用数组似乎是唯一的解决方案,但对于 React 应用程序来说也不是很令人满意。

我没有办法管理我的应用程序的状态。

有没有更好的方法来存储这个Rectangle 实例数组? 还是在react中使用这种对象设计根本不可能?

【问题讨论】:

"每次更新状态时创建一个新对象完全违背了使用此类的目的" - 为什么?让translate 返回一个新实例听起来非常有用。 没想到,但我每秒刷新画布 60 次。每次我移动一个矩形时创建一个新实例似乎真的很重性能。 您是自己制作动画帧,还是尝试让 React 重新绘制画布? 在这种情况下,确实没有理由将它们放在state 中——你不需要 React 来查看它们。只需将矩形数组直接存储在您的实例上,就像使用 .canvas.ctx 一样。 您还可以让矩形根据用例处理自己的绘图。就像在画布绘制时在矩形上调用更新方法,如果需要,让矩形绘制/移动/调整大小/平移。这样逻辑就更符合形状的目的 【参考方案1】:

困难的部分是弄清楚是否真的需要可变性(例如,出于性能原因),或者在经过更多思考(以及睡个好觉)之后,状态是否可以存在于 React 中。 p>

如果需要可变性,应用程序的该部分可以作为 Uncontrolled Component 存在于 React 之外(状态可以存在于 DOM 或一些将更新 DOM 的命令式代码中)。

来自 React 的父组件可以通过使用 ref 来访问超出反应的可变状态(来自事件处理程序或生命周期方法)(请参阅上面的文章)。一个简化的例子,使用 useRef 钩子:

const rndColor = () => `rgb($Array.from(Array(3)).map(() => Math.random()*255).join(','))`

const App = () => 
  const canvasRef = React.useRef()
  const handleClick = () => 
    // --> rect.translate(x: 10, y: 20) can be called here <--
    canvasRef.current.style.background = rndColor()
  
  return <canvas ref=canvasRef onClick=handleClick />


ReactDOM.render(<App />,document.getElementById('root'))
canvas 
  width: 150px;
  height: 150px;
  background: orange;
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

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

【讨论】:

以上是关于我可以做些啥来存储类的实例并在反应应用程序的上下文中管理状态的主要内容,如果未能解决你的问题,请参考以下文章

可以做些啥来防止类似论坛的应用程序中的垃圾邮件?

查询很慢,我可以做些啥来改进?

我可以做些啥来加快 S3 上传/更新?

在尝试将我的 iOS 应用程序提交到 App Store 时,我可以做些啥来克服下面列出的多个错误?

我可以做些啥来加快这个 SQL 查询的速度?

我可以做些啥来优化适用于 Postgres 和 MySQL 的 SQL 查询?