When using both React's forwardRef and callback ref, the ref can sometimes become undefined

This issue is not related to Mapbox, and no map or geographical knowledge is required to assist. The problem at hand pertains to React refs.

Currently, I am utilizing react-map-gl (Mapbox for React) in conjunction with @mapbox/mapbox-gl-draw to enable users to draw shapes on the map. Within this application, there are two key components: DrawControl, which facilitates drawing capabilities, and AllPastures, responsible for rendering the map and managing created/updated/deleted geometries.

When a user draws a polygon, it triggers the onCreate method within the DrawControl, subsequently executing onCreateOrUpdate. To pass the Draw reference from DrawControl to AllPastures, I am using forwardRef.

The following snippet showcases the DrawControl component:

export const DrawControl = React.forwardRef<MapboxDraw, DrawControlProps>(
  (props: DrawControlProps, ref) => {
    const drawRef: MapboxDraw = useControl<MapboxDraw>(
      () => new MapboxDraw(props),
      ({ map }) => {
        map.on('draw.create', props.onCreate);
        map.on('draw.update', props.onUpdate);
        map.on('draw.delete', props.onDelete);
        map.on('draw.modechange', props.onModeChange);
      },
      ({ map }) => {
        map.off('draw.create', props.onCreate);
        map.off('draw.update', props.onUpdate);
        map.off('draw.delete', props.onDelete);
        map.off('draw.modechange', props.onModeChange);
      },
      {
        position: props.position,
      }
    );

    // exposing drawRef outside the component by Ref
    React.useImperativeHandle(ref, () => drawRef, [drawRef]);

    return null;
  }

Meanwhile, part of the AllPastures component looks like this:

export const AllPastures = () => {
  ...
  const [drawRef, setDrawRef] = React.useState<MapboxDraw>();

  // utilizing callback ref to respond to drawRef changes
  const onDrawRef = React.useCallback((ref: MapboxDraw | null) => {
    if (ref) {
      setDrawRef(ref);
    }
  }, []);

  React.useEffect(() => {
    console.log('useEffect drawRef when app is loading', drawRef); // confirming that drawRef is defined here
  }, [map, drawRef]);

  const onCreateOrUpdate = (e: { features: Feature[] }) => {
    console.log('drawRef under onCreateOrUpdate method', drawRef); // question arises as to why drawRef is undefined here
  };

  ...

  return (
      <DrawControl
        ref={onDrawRef}
        position="top-right"
        displayControlsDefault={false}
        controls={{
          polygon: true,
          trash: true,
        }}
        defaultMode="simple_select"
        onCreate={onCreateOrUpdate}
        onUpdate={onCreateOrUpdate}
      />
  );
};

The main dilemma revolves around determining why the drawRef is undefined within the onCreateOrUpdate method?


To experiment with the issue firsthand, you can access a sandbox simulation via the following link: https://stackblitz.com/edit/vitejs-vite-bvutvb?file=src%2FAllPastures.tsx

Simply draw any polygon on the map and observe if the console log displays

drawRef under onCreateOrUpdate method
being undefined.

Remember to refresh the page (F5) after making code modifications to test the changes.

Answer №1

If you're searching for a solution, I discovered a way to pass the drawRef out of the DrawControl component using forwardRef, useImperativeHandle, and connecting that ref to my <DrawControl>.

You can find a functional example below, along with a comprehensive solution at: https://stackblitz.com/edit/vitejs-vite-bvutvb?file=src%2FDraw.tsx

export const Draw = () => {
  const { current: map } = useMap();
  const drawRef = React.useRef<MapboxDraw>(null);

  if (!map) {
    return null;
  }

  React.useEffect(() => {
    // This is where drawRef is not undefined
    console.log('useEffect drawRef when app is loading', drawRef);
  }, [drawRef]);

  const onCreateOrUpdate = React.useCallback(
    (e: { features: Feature[] }) => {
      // At this point, drawRef will not be undefined
      console.log('drawRef under onCreateOrUpdate method', drawRef);
    },
    [drawRef]
  );

  return (
    <DrawControl
      ref={drawRef}
      position="top-right"
      displayControlsDefault={false}
      controls={{
        polygon: true,
        trash: true,
      }}
      defaultMode="simple_select"
      modes={{ ...MapboxDraw.modes, draw_rectangle: DrawRectangle }}
      onCreate={onCreateOrUpdate}
      onUpdate={onCreateOrUpdate}
    />
  );
};

Answer №2

function handleCreateOrUpdate(e: { items: Item[] }) {
  // At this point, drawRef should always have a value
  console.log('drawRef is not undefined within the handleCreateOrUpdate function', drawRef);
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

ReactJS application encountering CORS error while accessing endpoint in Spring Boot Application secured with Keycloak

I am currently working on integrating ReactJS and Java Spring Boot applications, both protected by Keycloak 11.0.2. Keycloak is running on port 8083, ReactJS on port 3000, and the Spring Boot application on port 8085. However, when I attempt to utilize th ...

How to avoid displaying calculations within jsx when using React

Currently, I am in a map function where I am calculating the number of items for each category and then assigning this value to a property. The issue arises when this value is also displayed on the screen, which is something I want to avoid. ...

Implementing Text Input Props in MUI: A Step-by-Step Guide

Hey there! I'm currently working on a text field that includes InputProps, specifically an icon that triggers an onClick event. <TextField id="outlined-basic" label="Type Tag Name" variant="outline ...

Select multiple options by checking checkboxes in each row using React

Is it possible to display multiple select checkboxes with more than one per row? For example, I have four options [a, b, c, d]. How can I arrange it to have 2 options per row, totaling 2 rows instead of having 1 option per row for 4 rows? ☑a ☑b ☑c ...

How can I turn off autocomplete in MUI textfields?

Currently, I am working with the latest version of mui. Within my user contact info form, there is a zip code field that I do not want to be auto completed if the value is null. However, despite my efforts, it continues to autocomplete with the email saved ...

Why does i18next only function on http://localhost:3000/de (landing page) in NextJS and not on http://localhost:3000/de/about?

I'm new to this and feeling lost on how to tackle this issue. I tried following the instructions on https://github.com/i18next/next-i18next, but I'm struggling with index.js. When I toggle /de on my landing page, it translates correctly in the UR ...

Deactivate a input field depending on an autocomplete selection in React.js using Material-UI

Currently, I am facing an issue with two fields on my form. The first field is an autocomplete feature while the second field is a textfield. By default, my second field is disabled but I need it to become enabled every time a user types and selects an opt ...

When using express, the response.sendFile() function is causing an Uncaught SyntaxError with the error message: Unexpected token <

Currently, I'm utilizing react-router with browserHistory. There is a specific function in my code that is meant to send the Index.html file since the routing is handled client-side with react-router. However, there seems to be an issue where the serv ...

What is the best way to create a compound query in Firebase?

I am working on a TypeScript script to search for a city based on its population... import { getFirebase } from "react-redux-firebase"; ... get fb() { return getFirebase(); } get fs() { return this.fb.firestore(); } getCollection(coll ...

I want the name of the hospital to be displayed in the dropdown menu with a shortened version of the hospital's name appearing

Check out my code snippet: here import Autocomplete from "@mui/material/Autocomplete"; import TextField from "@mui/material/TextField"; import React, { useState } from "react"; const hospitalList = { docs: [ { _id: ...

What methods are available for altering state in Server Actions in NextJS 13?

Struggling to Implement State Change in NextJS 13 Server Actions I have encountered a challenge with altering state within the execution of server actions in NextJS 13. The scenario involves an actions.ts file located at the root of the app directory. Cur ...

Encountering an issue where trying to read the 'useContext' property of null in a React-Redux environment after importing MUI components

I've been troubleshooting this issue for some time now, but haven't had any success. My react-redux app is quite basic, as shown below: import React from 'react'; import logo from './logo.svg'; import GoogleMapReact from &apo ...

React component rendering twice due to width awareness

In a React component that I've developed, I have utilized ResizeObserver to track its own width. Within this component, two divs are rendered with each having a "flex: 1" property to ensure equal width distribution. Under certain conditions, such as w ...

What is the best way to send a reference to a sibling component in React?

In my project, I am faced with the challenge of using a ref on a search input within my Header component which is not a higher order component to my ResultsList component. The goal is to focus on the Header's search input from the ResultsList componen ...

What are the potential drawbacks of directly modifying the state in ReactJS?

According to the documentation, it is stated that An example provided explains how directly modifying state will not re-render a component: // Incorrect this.state.comment = 'Hello'; Instead, the correct way is to use setState(): // Correct ...

When conducting a test, it was found that the JSX element labeled as 'AddIcon' does not possess any construct or call signatures

Here is a code snippet I'm currently working with: const tableIcons: Icons = { Add: forwardRef((props, ref) => <AddBox {...props} ref={ref} />), Check: forwardRef((props, ref) => <Check {...props} ref={ref} />) }; const AddIcon ...

When switching from page 1 to page 2 in a React MUI Data table, the cell data does not update to reflect the API response

Imagine a scenario where there is a parent component containing a child component that acts as a MUI data table. This child component receives API response data from the parent component as props. Problem: The data in the table loads perfectly on initial ...

The conditional statement in the given code snippet is failing to execute properly

const checkCondition = (props) => { let conditionMet = false; console.log('-----****----',props); if(props && props.isAllowed) { conditionMet = true; } if(someOtherCondition) { return( <li><Link className=" ...

React: Challenges with Implementing Conditional Rendering

In my React application, I am utilizing the Firebase SDK. Whenever a user needs to reset their password, they will be directed to a specific page within my app. If the entered code is valid, the <PWResetConfirmForm /> component should be displayed. I ...

Issue: When calling setState(...), you must provide either an object containing the state variables to update or a function that will return an object with the updated

Encountering an error in the else section with this.state.incorrect + 1 and this.state.correct + 1 I've come across a similar issue that wasn't resolved by visiting this link: React Native: setState(...): takes an object of state variables to up ...