我可以做些啥来存储类的实例并在反应应用程序的上下文中管理状态
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>
【讨论】:
以上是关于我可以做些啥来存储类的实例并在反应应用程序的上下文中管理状态的主要内容,如果未能解决你的问题,请参考以下文章