1

我有一个巨大的游戏对象,它在渲染时附加到画布上。我想在创建组件时只创建一次,直到组件被销毁才删除它。

(如果游戏是定期创建和销毁的,那么用户将失去他们的游戏状态。假设我不想编辑游戏以将存储放入ContextRedux或其他东西中,并希望保持游戏原样..)

我看到至少三种方法可以做到这一点:

function Game1() {
    const [world, _] = useState(createWorld) // unused setter
    useEffect(() => world.destroy, [])
    const attachCanvas = useCallback(canvas => world.start(canvas))
    return <canvas ref={attachCanvas}/>
}

function Game2() {
    const [world, setWorld] = useState()
    useEffect(() => {
        const newWorld = createWorld()
        setWorld(newWorld)
        return newWorld.destroy
    }, []) // <- adding setWorld and newWorld dependencies can create loops
    const attachCanvas = useCallback(canvas => world.start(canvas))
    return <canvas ref={attachCanvas}/>
}

function Game3() {
    // Works but docs say that a memo may be called twice if the engine feels like it
    const world = useMemo(createWorld, []) 
    ...
}

有什么问题Game1吗?有没有第四种更好的方法来创建和销毁状态一次?

4

4 回答 4

2

你的第一个例子很好。

但是,有时编写基于类的 React 组件更容易。您可以使用类构造函数将您的游戏世界初始化为 Class 成员属性,然后使用 componentWillUnmount 生命周期挂钩将其销毁。

class Game extends React.Component {
  constructor(props) {
    super(props);

    this.canvasRef = React.createRef();
    this.world = createWorld();
  }

  componentDidMount() {
    this.world.start(canvasRef.current);
  }

  componentWillUnmount() {
    this.world.destroy();
  }

  render() {
    return <canvas ref={canvasRef} />;
  }
}
于 2021-07-06T20:45:19.403 回答
0

我发现的最后一个选项是编写自定义钩子或使用预先存在的钩子,例如也允许依赖项ahooksuseCreation 。一个简单的自定义钩子可能如下所示:

function useOnce(factory, makeCleanup) {
    const [x] = useState(factory)
    useEffect(makeCleanup(x), [])
    return x
}
于 2021-07-06T22:58:32.457 回答
0

No, it's fine to create a permanent state once and never update it. You don't need to deconstruct the setter either, so your code could be const [world] = useState(createWorld);

于 2021-07-06T20:38:00.680 回答
-1

这是在 React 钩子中创建和销毁变量的最佳方式。

function Game1(props) {
  const [world, setWorld] = useState("");
  // Similar to componentDidMount and componentDidUpdate
  useEffect(() => {
    setWorld("hello"); // Now world will be equals to "hello"
  });
  // Similar to componentWillUnmount
  useEffect(() => {
    // Specify how to clean up after this effect:
    return function cleanup() {
      setWorld(""); // Reset state here
    }
  });
}
于 2021-07-06T21:18:36.153 回答