cleanup: UNSAFE_componentWillReceiveProps -> modern lifecycles (React 19)

The 4 class components on UNSAFE_componentWillReceiveProps converted:
- FromNow, FireStats: state is a pure mirror of props -> getDerivedStateFromProps
- Fires: split the derived state (getDerivedStateFromProps) from the URL-
  canonicalization side effect (componentDidUpdate, guarded by prevProps) so
  the side effect never runs inside the pure gDSFP
- SelectionMap: marker is also locally draggable, so gDSFP would clobber a
  drag -> componentDidUpdate guarded on a real center/distance value change
  (no clobber, no setState loop), merged with the existing fit()

Browser-verified: /fires count, fire detail + FromNow, active-fire URL
canonicalizes to /fire/archive/:id, home SelectionMap renders stably (no
render loop). No UNSAFE_ warnings remain from our code. REST smoke
byte-identical.
This commit is contained in:
vjrj 2026-07-21 14:03:44 +02:00
parent 4ddaa0d8ec
commit 229b2cb233
4 changed files with 48 additions and 46 deletions

View file

@ -60,18 +60,22 @@ class SelectionMap extends Component {
}
}
UNSAFE_componentWillReceiveProps(nextProps) {
const nextCenter = nextProps.center[0] ? nextProps.center : this.state.center;
const nextMarker = nextProps.center[0] ? nextProps.center : this.state.marker;
this.setState({
center: nextCenter,
marker: nextMarker,
distance: nextProps.distance || this.state.distance
});
// this.fit();
}
componentDidUpdate() {
// Was UNSAFE_componentWillReceiveProps: pull center/distance from props into
// state when they actually change. Done in componentDidUpdate (not
// getDerivedStateFromProps) because the marker is also locally draggable, so
// we must guard on a real value change to avoid clobbering a drag / looping.
componentDidUpdate(prevProps) {
const { center, distance } = this.props;
const centerChanged = center[0] &&
(center[0] !== this.state.center[0] || center[1] !== this.state.center[1]);
const distanceChanged = distance && distance !== this.state.distance;
if (centerChanged || distanceChanged) {
this.setState({
center: center[0] ? center : this.state.center,
marker: center[0] ? center : this.state.marker,
distance: distance || this.state.distance
});
}
this.fit();
}