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

@ -37,18 +37,25 @@ class Fire extends React.Component {
};
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props.when !== nextProps.when || this.props.loading !== nextProps.loading || this.props.notfound !== nextProps.notfound) {
// console.log(`Next when ${nextProps.when}`);
if (nextProps.fire && (nextProps.alert || nextProps.active || nextProps.fromHash)) {
// change url to archive with new _id
nextProps.history.replace(`/fire/archive/${hexId(nextProps.fire._id)}`);
// state mirrors props purely for shouldComponentUpdate (render reads props
// directly). Was the derived-state half of UNSAFE_componentWillReceiveProps.
static getDerivedStateFromProps(props, state) {
if (props.when !== state.when || props.loading !== state.loading || props.notfound !== state.notfound) {
return { loading: props.loading, notfound: props.notfound, when: props.when };
}
return null;
}
componentDidUpdate(prevProps) {
// side effect (must not run in getDerivedStateFromProps): once the fire is
// resolved via alert/active/hash, canonicalize the URL to /fire/archive/:id
if (prevProps.when !== this.props.when || prevProps.loading !== this.props.loading || prevProps.notfound !== this.props.notfound) {
const {
fire, alert, active, fromHash, history
} = this.props;
if (fire && (alert || active || fromHash)) {
history.replace(`/fire/archive/${hexId(fire._id)}`);
}
this.setState({
loading: nextProps.loading,
notfound: nextProps.notfound,
when: nextProps.when
});
}
}