1

Following the offical reference for Higher Level Component Factory to update props for a Control Component

The core APIs export other high-level component factories that can be used in a similar way.

I've mimicked the example - but I get a syntax error for the following:

import L from "leaflet";
import "leaflet-routing-machine";
import { createControlComponent } from "@react-leaflet/core";
import 'leaflet-routing-machine/dist/leaflet-routing-machine.css'

function setWaypoints(props)
{
    return { 
        waypoints: [
        L.latLng(props.startLat, props.startLng),
        L.latLng(props.endLat, props.endLng)
        ],
        lineOptions: {
            styles: [{ color: "#0500EE", weight: 4 }]
        },
        show: false,
        addWaypoints: false,
        routeWhileDragging: true,
        draggableWaypoints: true,
        fitSelectedRoutes: true,
        showAlternatives: false,
        createMarker: function() { return null; },

    }
}


function createRoutingMachine(props, context) 
{
    const instance =  new L.Routing.control(setWaypoints(props))
    return 
    { 
        instance, context: { ...context, overlayContainer: instance }    
    }
}


function updateRoutingMachine(instance, props, prevProps)
{
    if (props.endLat !== prevProps.endLat || props.endLng !== prevProps.endLng) 
    {
        instance.setWaypoints(props)
    }
}

const RoutingMachine = createControlComponent(createRoutingMachine, updateRoutingMachine)
export default RoutingMachine;

Missing semicolon. (35:25)

33 | return 34 | {

35 | instance, context: { ...context, overlayContainer: instance }
| ^ 36 | }

If I change this to:


function createRoutingMachine(props) 
{
    const instance =  new L.Routing.control(setWaypoints(props))
    return instance
}

The compiler is happy, but the component never updates.

I know I'm creating the Control Component incorrectly, but I can't find the information for the correct implementation.

Related:
How to use Leaflet Routing Machine with React-Leaflet 3?
How to extend TileLayer component in react-leaflet v3?

1 Answer 1

7

You will notice that in the docs, createcontrolcomponent lists only one argument, which is a function to create the instance. You are expecting it to behave like createlayercomponent, which takes two arguments. In createlayercomponent, the second argument is a function to update the layer component when the props change. However, createcontrolcomponent offers no such functionality. react-leaflet is assuming, much like vanilla leaflet, that once your control is added to the map, you won't need to alter it directly.

This gets a bit confusing in terms of leaflet-routing-machine, because you don't need to change the instance of the control, but rather you need to call a method on it which affects the map presentation.

IMO, the best way to go is to use a state variable to keep track of whether or not your waypoints have changed, and use a ref to access the underlying leaflet instance of the routing machine, and call setWayPoints on that:

// RoutineMachine.jsx

const createRoutineMachineLayer = (props) => {
  const { waypoints } = props;
  const instance = L.Routing.control({
    waypoints,
    ...otherOptions
  });

  return instance;
};

// Takes only 1 argument:
const RoutingMachine = createControlComponent(createRoutineMachineLayer);
// Map.jsx

const Map = (props) => {
  // create a ref
  const rMachine = useRef();
  // create some state variable, any state variable, to track changes
  const [points, setPoints] = useState(true);
  const pointsToUse = points ? points1 : points2;

  // useEffect which responds to changes in waypoints state variable
  useEffect(() => {
    if (rMachine.current) {
      rMachine.current.setWaypoints(pointsToUse);
    }
  }, [pointsToUse, rMachine]);

  return (
    <MapContainer {...props}>
      <RoutineMachine ref={rMachine} waypoints={pointsToUse} />
      <button onClick={() => setPoints(!points)}>
        Toggle Points State and Props
      </button>
    </MapContainer>
  );
};

Working Codesandbox

Bonus: a cheap and easy way to force a rerender on your <RoutineMachine> component (or any react component) is to assign it a key prop, and change that key prop when you want to rerender it. This might be a uuid, or even a unique set of waypoints ran through JSON.stringify. Just an idea.

Sign up to request clarification or add additional context in comments.

2 Comments

This just saved me hours of debugging. It worked out of the box
Nice pointer - in my case some of the props on react-leaflet Locate control need changing dynamically so I had to retain a ref as suggested

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.