0

I'm trying to use a useContext hook inside a react-leaflet controlComponent but I have an error when my context fires the update function.

I use a react-leaflet controlComponent because of leaflet routing machine. I think the code + the error are better than word:

MainBoard.tsx

export const CartographyContext: React.Context<CartographyContextType> = React.createContext<CartographyContextType>({ positions: [] });
...

const routeSummaryValueContext = React.useMemo(
  () => ({ routeSummary, setRouteSummary }),
  [routeSummary]
);

const elevationProfileValueContext = React.useMemo(
  () => ({ elevationProfile, setElevationProfile }),
  [elevationProfile]
);

........

<CartographyContext.Provider value={{ positions, elevationProfileValueContext, routeSummaryValueContext, positionsValueContext, addPosition, changePosition }}>
.........
<RoutingMachine
   orsOptions={{
        ....
   }} />
..........
</CartographyContext.Provider>

RoutingMachine.tsx:

const CreateRoutineMachineLayer = (props: any) => {
    const geoService = new GeoLocalisationService();
    const cartographyContext: CartographyContextType = React.useContext<CartographyContextType>(CartographyContext);
    const [routes, setRoutes] = React.useState<any[]>();

    React.useEffect(() => {
        if (routes) {
             //The line which cause the error
cartographyContext.elevationProfileValueContext.setElevationProfile(geoService.getElevationProfile(decodePolyline(routes[0].geometry, true)));
            const summary: RouteSummary = {
                ascent: routes[0].routeSummary.ascent,
                descent: routes[0].routeSummary.descent,
                distance: routes[0].routeSummary.distance,
                estimatedDuration: routes[0].routeSummary.duration
            }
            cartographyContext.routeSummaryValueContext.setRouteSummary(summary);
        }
    }, [routes]);

    const { orsOptions } = props;

    const instance = L.Routing.control({
        router: new OpenRouteRouter(orsOptions),
        lineOptions: {
            styles: [{ color: "#3933ff", weight: 4 }],
            extendToWaypoints: true,
            missingRouteTolerance: 0
        },
        routeWhileDragging: true,
        autoRoute: true,
        geocoder: new geocoder.Geocoder(),

    }).on('routesfound', (e) => {
        setRoutes(e.routes);
    });

    useMapEvents({
        click: (e: L.LeafletMouseEvent) => {
            if (instance.getWaypoints().length === 2 && instance.getWaypoints()[0].latLng == null) {
                instance.spliceWaypoints(0, 1, new L.Routing.Waypoint(e.latlng, null, {}));
            } else if (instance.getWaypoints().length === 2 && instance.getWaypoints()[1].latLng == null) {
                instance.spliceWaypoints(1, 1, new L.Routing.Waypoint(e.latlng, null, {}));
            } else {
                instance.spliceWaypoints(instance.getWaypoints().length, 0, new L.Routing.Waypoint(e.latlng, null, {}));
            }
        }
    });


    return instance;
};

const RoutingMachine = createControlComponent(CreateRoutineMachineLayer);

error :

g: React has detected a change in the order of Hooks called by ForwardRef(LeafComponent). This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks

   Previous render            Next render
   ------------------------------------------------------
1. useContext                 useContext
2. useRef                     useRef
3. useContext                 useRef
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


..............
Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.

I clearly doing something wrong here but I haven't found yet.

Thank you Kind regards

1
  • Could you share some more code, namely for LeafComponent? The error indicates you may be violating the Rules of Hooks with a possible early return before a hook Commented Nov 28, 2022 at 12:43

1 Answer 1

0

Ok I found the good implementation :

const RoutingMachine: React.FC<RoutingMachineProps> = (props) => {
    //const RoutineMachine = (props: any) => {
    const geoService = new GeoLocalisationService();
    const cartographyContext: CartographyContextType = React.useContext<CartographyContextType>(CartographyContext);
    const [instance, setInstance] = React.useState<any>();
    const [alreadyDisplayed, setAlreadyDisplayed] = React.useState(false);

    const { orsOptions } = props;

    const map = useMap();

    //const instance = L.Routing.control({
    React.useEffect(() => {
        const instance = L.Routing.control({
            router: new OpenRouteRouter(orsOptions),
            lineOptions: {
                styles: [{ color: "#3933ff", weight: 4 }],
                extendToWaypoints: true,
                missingRouteTolerance: 0
            },
            routeWhileDragging: true,
            autoRoute: true,
            geocoder: (L.Control as any).Geocoder.google({
                apiKey: GOOGLE.googleMapApiKey,
            }),

        }).on('routesfound', (e) => {
            const routes = e.routes;
            cartographyContext.setElevationProfile(geoService.getElevationProfile(decodePolyline(routes[0].geometry, true)));
            const summary: RouteSummary = {
                ascent: routes[0].routeSummary.ascent,
                descent: routes[0].routeSummary.descent,
                distance: routes[0].routeSummary.distance,
                estimatedDuration: routes[0].routeSummary.duration
            }
            cartographyContext.setRouteSummary(summary);
        })
        setInstance(instance);
        instance.addTo(map);
    }, []);

    useMapEvents({
        click: (e: L.LeafletMouseEvent) => {
            if (instance) {
                if (instance.getWaypoints().length === 2 && instance.getWaypoints()[0].latLng == null) {
                    instance.spliceWaypoints(0, 1, new L.Routing.Waypoint(e.latlng, null, {}));
                } else if (instance.getWaypoints().length === 2 && instance.getWaypoints()[1].latLng == null) {
                    instance.spliceWaypoints(1, 1, new L.Routing.Waypoint(e.latlng, null, {}));
                } else {
                    instance.spliceWaypoints(instance.getWaypoints().length, 0, new L.Routing.Waypoint(e.latlng, null, {}));
                }
            }
        }
    });

    return null;
};


export default RoutingMachine;
Sign up to request clarification or add additional context in comments.

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

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.