@destroyed
Runs while the component is being removed. Whatever it set up that the framework does not own comes undone here.
The situation it is for
A price ticker on a socket. The component opens the connection when it appears and has to close it when it goes — because nothing else will, and a socket left open keeps receiving, keeps calling back, and keeps a component alive that the page has already forgotten:
class Ticker extends Component<{ symbol: string }> {
@state price = "—";
private socket?: WebSocket;
@mounted
connect() {
const socket = new WebSocket("wss://example.test");
socket.onmessage = (e) => {
this.price = String(e.data);
};
this.socket = socket;
}
@destroyed
disconnect() {
this.socket?.close();
}
render() {
return <p>{this.props.symbol} {this.price}</p>;
}
}
Take the @destroyed away and the page still works — which is the trouble. The socket keeps
arriving, this.price = … keeps being called on a component nobody can see, and the only sign is a
report (RMD008) that a state write had nowhere to go.
What you do NOT have to undo
Most of it. A great deal of cleanup that other frameworks ask for is already done:
- State, computes and props go with the component.
@intervaland@timeout, and theInterval/Timeouthooks, clear themselves.@onWindowand@onDocumentremove their listeners.- A subscription decorator runs the cleanup its connect returned.
What is left is what you reached for yourself: a socket, an observer, a handle from a library that is not Ramonda's.
Running on one side only
It takes env, like @created. A server render tears nothing
down, so this is a browser moment in practice.
What it refuses
Anything but a method.
What it costs
Nothing to declare it. What it costs to get wrong is a leak: something that outlives the component
and writes state into it is reported as RMD008 — the write is
dropped, the render it asked for never happens, and the only sign is a value that stopped moving.
Next
- Lifecycle — all four moments, in order.
- Your own decorators — a subscription that cleans up without this.