javascript - 我可以做些什么来存储类的实例并在反应应用程序的上下文中管理状态
问题描述
我正在使用这个 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 中使用这种对象设计是根本不可能的?
解决方案
困难的部分是弄清楚是否真的需要可变性(例如出于性能原因),或者在给予更多思考(以及睡个好觉)之后,状态是否可以存在于 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>
推荐阅读
- 3d - SceneKit:关于定位相机节点的数学问题
- apache-kafka - 分支到多个主题时的 Kafka Streams 架构问题
- c - 连续字符串替换导致 C 中的无效读取
- reactjs - 将 redux observable 与 axios 一起使用
- bash - “非法选项-n”和“[[:未找到”带有答案选项
- bash - 如何创建 bash 脚本来执行一些其他操作
- python - 即使没有数据,如何生成excel文件。Python 熊猫 ExcelWriter
- azure - Azure VMSS - 通过 REST API 扣除正常运行时间
- sql - SQL查询如何删除table2中不活动的表
- java - 如何创建一个循环,反复提示用户输入blast、punch 或kick 进行攻击或Do It!退出循环