refactored to use reducer instead of explicit state change in component

main
Inga 🏳‍🌈 1 year ago
parent 6dab7c18cc
commit 748334108c
  1. 119
      src/routePlanner/index.tsx
  2. 4
      src/routePlanner/marker.tsx
  3. 79
      src/routePlanner/reducer.ts
  4. 20
      src/routePlanner/types.ts

@ -1,96 +1,71 @@
import { nanoid } from 'nanoid'; import { useCallback, useReducer } from 'preact/hooks';
import { useCallback, useState } from 'preact/hooks';
import { reorderElements } from '../shared/collections';
import { Coordinates } from '../shared/types'; import { Coordinates } from '../shared/types';
import { ExportComponent } from './export'; import { ExportComponent } from './export';
import { MapComponent } from './map'; import { MapComponent } from './map';
import { MarkersComponent } from './markers'; import { MarkersComponent } from './markers';
import { Marker, ReorderMarkersParams } from './types'; import { ReorderMarkersParams } from './types';
import './style.css'; import './style.css';
import { markersReducer } from './reducer';
export const RoutePlanner = () => { export const RoutePlanner = () => {
const [markers, setMarkers] = useState<Marker[]>([]); const [markers, dispatchMarkers] = useReducer(markersReducer, []);
const onMarkersReorder = useCallback( const reorderMarkers = useCallback(
({ oldIndex, newIndex }: ReorderMarkersParams): void => ({ oldIndex, newIndex }: ReorderMarkersParams): void =>
setMarkers((oldMarkers) => { dispatchMarkers({
console.log( type: 'reorder',
`Reordering markers: from ${oldIndex} to ${newIndex}`, data: { oldIndex, newIndex },
);
const newMarkers = reorderElements(
oldMarkers,
oldIndex,
newIndex,
);
return newMarkers.map((marker, i) => ({
...marker,
label: `${i + 1}`,
remove: () =>
onMarkersReorder({
oldIndex: i,
newIndex: -1,
}),
...(i - 1 >= 0 && {
moveDown: () =>
onMarkersReorder({
oldIndex: i,
newIndex: i - 1,
}),
}),
...(i + 1 < newMarkers.length && {
moveUp: () =>
onMarkersReorder({
oldIndex: i,
newIndex: i + 1,
}),
}),
}));
}), }),
[], [],
); );
const onMapClick = useCallback( const moveMarkerUp = useCallback(
(coordinates: Coordinates) => { (key: string) =>
setMarkers((markers) => [ dispatchMarkers({
...markers.slice(0, markers.length - 1), type: 'moveUp',
...markers data: { key },
.slice(markers.length - 1, markers.length) }),
.map((marker) => ({ [],
...marker, );
moveUp: () =>
onMarkersReorder({ const moveMarkerDown = useCallback(
oldIndex: markers.length - 1, (key: string) =>
newIndex: markers.length, dispatchMarkers({
}), type: 'moveDown',
})), data: { key },
{ }),
key: nanoid(10), [],
label: `${markers.length + 1}`, );
remove: () =>
onMarkersReorder({ const removeMarker = useCallback(
oldIndex: markers.length, (key: string) =>
newIndex: -1, dispatchMarkers({
}), type: 'remove',
...(markers.length && { data: { key },
moveDown: () => }),
onMarkersReorder({ [],
oldIndex: markers.length, );
newIndex: markers.length - 1,
}), const addMarker = useCallback(
}), (coordinates: Coordinates) =>
dispatchMarkers({
type: 'add',
data: {
coordinates, coordinates,
moveUp: moveMarkerUp,
moveDown: moveMarkerDown,
remove: removeMarker,
}, },
]); }),
}, [moveMarkerUp, moveMarkerDown, removeMarker],
[onMarkersReorder],
); );
return ( return (
<section class="route-planner"> <section class="route-planner">
<section class="markers"> <section class="markers">
<MarkersComponent <MarkersComponent
onMarkersReorder={onMarkersReorder} onMarkersReorder={reorderMarkers}
markers={markers} markers={markers}
/> />
</section> </section>
@ -98,7 +73,7 @@ export const RoutePlanner = () => {
<ExportComponent markers={markers} /> <ExportComponent markers={markers} />
</section> </section>
<section class="map"> <section class="map">
<MapComponent onMapClick={onMapClick} markers={markers} /> <MapComponent onMapClick={addMarker} markers={markers} />
</section> </section>
</section> </section>
); );

@ -5,10 +5,10 @@ export const MarkerComponent = ({ marker }: MarkerProps) => {
<li key={marker.key}> <li key={marker.key}>
{`Waypoint ${marker.label} (${marker.key.substring(0, 4)})`} {`Waypoint ${marker.label} (${marker.key.substring(0, 4)})`}
<span class="buttons"> <span class="buttons">
<button onClick={marker.moveDown} disabled={!marker.moveDown}> <button onClick={marker.moveUp} disabled={!marker.moveUp}>
🔼 🔼
</button> </button>
<button onClick={marker.moveUp} disabled={!marker.moveUp}> <button onClick={marker.moveDown} disabled={!marker.moveDown}>
🔽 🔽
</button> </button>
<button onClick={marker.remove}>🗑</button> <button onClick={marker.remove}>🗑</button>

@ -0,0 +1,79 @@
import { nanoid } from 'nanoid';
import { Marker, ReducerAction } from './types';
import { reorderElements } from '../shared/collections';
const reindexMarkers = (markers: Marker[]) =>
markers.map((marker, i) => {
const newLabel = `${i + 1}`;
if (marker.label === newLabel) {
return marker;
}
return {
...marker,
label: newLabel,
};
});
export const markersReducer = (
markers: Marker[],
action: ReducerAction,
): Marker[] => {
const type = action.type;
switch (action.type) {
case 'add': {
const key = nanoid(10);
return reindexMarkers([
...markers,
{
key,
label: 'placeholder',
remove: () => action.data.remove(key),
moveUp: () => action.data.moveUp(key),
moveDown: () => action.data.moveDown(key),
coordinates: action.data.coordinates,
},
]);
}
case 'remove': {
return reindexMarkers([
...markers.filter(({ key }) => key !== action.data.key),
]);
}
case 'moveUp': {
const oldIndex = markers.findIndex(
({ key }) => key === action.data.key,
);
if (oldIndex < 1) {
return markers;
}
return reindexMarkers(
reorderElements(markers, oldIndex, oldIndex - 1),
);
}
case 'moveDown': {
const oldIndex = markers.findIndex(
({ key }) => key === action.data.key,
);
if (oldIndex < 0 || oldIndex + 1 >= markers.length) {
return markers;
}
return reindexMarkers(
reorderElements(markers, oldIndex, oldIndex + 1),
);
}
case 'reorder': {
return reindexMarkers(
reorderElements(
markers,
action.data.oldIndex,
action.data.newIndex,
),
);
}
default:
throw new Error(`Unsupported type ${type}`);
}
};

@ -8,6 +8,26 @@ export type Marker = Waypoint & {
label: string; label: string;
}; };
type GenericReducerAction<TType extends string, TData> = {
type: TType;
data: TData;
};
export type ReducerAction =
| GenericReducerAction<
'add',
{
coordinates: Coordinates;
remove: (key: string) => void;
moveUp: (key: string) => void;
moveDown: (key: string) => void;
}
>
| GenericReducerAction<'remove', { key: string }>
| GenericReducerAction<'moveUp', { key: string }>
| GenericReducerAction<'moveDown', { key: string }>
| GenericReducerAction<'reorder', { oldIndex: number; newIndex: number }>;
export type ReorderMarkersParams = { export type ReorderMarkersParams = {
oldIndex: number; oldIndex: number;
newIndex: number; newIndex: number;

Loading…
Cancel
Save