首页 > 解决方案 > 我可以做些什么来存储类的实例并在反应应用程序的上下文中管理状态

问题描述

我正在使用这个 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 中使用这种对象设计是根本不可能的?

标签: javascriptreactjsobjectstate

解决方案


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

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

来自 React 的父组件可以通过使用 a 来访问超出反应的可变状态(来自事件处理程序或生命周期方法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>


推荐阅读