在 React 中创建和销毁一个东西的最佳方法是什么?(来自使用状态的未使用的二传手可以吗?



我有一个巨大的游戏对象,它在渲染时附着在画布上。我想在创建组件时只创建一次,而不是在组件被销毁之前删除它。

(如果游戏被周期性地创建和销毁,那么用户将失去他们的游戏状态。比方说,我不想编辑游戏以将存储放在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有什么问题吗?有没有第四种更好的方法来创建和摧毁一次状态?

您的第一个示例很好。

然而,有时编写一个基于类的React组件会更容易。您可以使用类构造函数将游戏世界初始化为类成员属性,然后使用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} />;
}
}

不,只创建一次永久状态而从不更新它是可以的。你也不需要解构setter,所以你的代码可以是const [world] = useState(createWorld);

我发现的最后一个选项是编写自定义挂钩或使用预先存在的挂钩,比如ahooks的useCreation,它也允许依赖关系。一个简单的自定义挂钩可能看起来像:

function useOnce(factory, makeCleanup) {
const [x] = useState(factory)
useEffect(makeCleanup(x), [])
return x
}

这是在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
}
});
}

最新更新