【React】100 行代码实现点灯小游戏💡

419 阅读2分钟

效果

demo:viewweiwu.github.io/light/
code:github.com/viewweiwu/l…

classDiagram
Animal <|-- Duck
Animal <|-- Fish
Animal <|-- Zebra
Animal : +int age
Animal : +String gender
Animal: +isMammal()
Animal: +mate()
class Duck{
+String beakColor
+swim()
+quack()
}
class Fish{
-int sizeInFeet
-canEat()
}
class Zebra{
+bool is_wild
+run()
}

规则

  • 9 盏灯,分为点亮和不点亮,白色不点亮,蓝色点亮。
  • 点击任意一盏灯,不仅本身会取反,上下左右的灯也会取反,点亮的会变成不点亮,不点亮的会点亮。
  • 所有的灯都被点亮即胜利。
  • 开场随机点亮灯。

实现思路

定义数据格式

let list = [
    [0, 1, 0],
    [0, 0, 0],
    [0, 1, 1]
]

这里使用了二维数组,就如同界面所展示的一样,3 行 3 列。另外用 0 表示关,1 表示开。

数据渲染

已经定义好数据格式,渲染就非常容易了。

list.map((row, i) => 
    row.map((item, j) => 
      <span key={ `${i}-${j}` } onClick={ () => handleClick(i, j) } className={`item ${item ? 'active' : ''}`}></span>
    )
)

事件绑定

给每一个开关绑定上单击事件。 先需要给开关取反工作,这里用了个小技巧,用取反的运算符。

1 ^ 0 // 1
1 ^ 1 // 0

这样子就可以给开关取反了。

const toggleItem = (newList, i, j) => {
  newList[i][j] = 1^newList[i][j]
}

之后给除了本身之外的,上下左右四个方向取反就 ok 了。

胜利校验

校验胜利非常简单,只要判断 list 所有的数据全是 1,即是胜利。用 for 循环是方便中断循环,forEach,every 循环都不能中断。

const checkList = (newList) => {
  for (let i = 0; i < newList.length; i++) {
    for (let j = 0; j < newList[i].length; j++) {
      if (newList[i][j] === 0) {
        return false
      }
    }
  }
  return true
}

另外就是触发校验的时机了。react hook 里面可以用 useEffect 来保证数据渲染完成后触发事件。那么这里就使用这种方式。第二个参数表示要监测的参数变化。

useEffect(() => {
  let result = checkList(list)
  if (result) {
    alert('win')
  }
}, [list])

开局

刚开始定义的数据,所有的开关都是 0 || 1 ,让每次开局都是不一样的.

const getRandom = (min, max) => {
  return Math.floor(Math.random() * 2)
}

const getRandomData = () => {
  let list = []
  for (let i = 0; i < 3; i++) {
    let row = []
    for (let j = 0; j < 3; j++) {
      row.push(getRandom())
    }
    list.push(row)
  }
  return list
}

总结

  • react hook 相比以前 ReactComponent 会减少很多的定义。
  • react hook 里没有 setState,所以也没有 setState 后的回调,需要用 useEffect 替代实现。
  • 开关取反可以使用 ^ 运算符,可以很方便让数字的取反。
  • 使用跟画面一一对应的数据格式,后面操作思路会清晰很多。