React Sharing Data Between Components without reloading component
New here? Learn about Bountify and follow @bountify to get notified of new bounties! x

I have a map component, where the user will select images - i want to display a list of these points in a different component on the side... i thought the way to do this is using creating a context component, and passing the functions as values in the provider

import React, { useState, createContext } from "react";
export const ImageContext = createContext(); 
export const ImageProvider = (props) => {
    const [images, setImages] = useState([]);
    return <ImageContext.Provider value={[images,setImages]} >
    {props.children}
    </ImageContext.Provider>;
 };

wrapping my app in the contextprovider

return (
<div id="App">
        <ImageProvider>
             ...
             <SidePanel />
              <WebMapView />
             ...
          </ImageProvider>
    </div>
  );
}

i import into my mapcomponent the context and Usecontext as below

import { ImageContext } from "./ImageContext";
const [images, setImages] = useContext(ImageContext);

then setting the context as below when the user makes input to the map

export const WebMapView = () => {
...
    setImages(results.features);
...
}

problem:

  1. as the map loads the data it populates the context and calls the function, which updates the state, this causes the mapcomponent to reload, restarting the process... the map is never usable... reloading the map component is never good because it would mean needing to save the state of everything in the map, then reloading all those data layers etc using that state - this will adversely affect the user experience with no benefits

goals:

  1. i need to get the data and pass it to the sidecomponent without reloading the map so it becomes usable
  2. i need to get selection data on the map and pass it TO the sidecomponent each time the user makes an input without reloading the map, but keeping the normal functionality of react reloading the sidepanel when new data is available in the state
  3. i need to pass information FROM the sidepanel to the mapcomponent to process within the map, but without reloading the map...

i get the impression context is not the right tool for this data sharing requirement, so i am hoping somebody has a suggestion how to do this?
i am using the latest react v16, so full availability of all the new functionality etc,

Some working code could be really useful at this point. Can you share a Codesandbox with some minimal working example to work on?
kostasx 1 month ago
hey Kostasx, please see here - https://6o6bf.csb.app/ when you zoom in the map, watch the numbers on the left panel, they change as it should be, but then they change back when the map resets because of reload... (this is not the actual data i will be sharing, will be points selected on map, but i think it explains the problem)
weaverk 1 month ago
there is no sample code for goal 3, because i didn't get that far yet, i will just need to pass array of numbers back to map component... without reload... so i can use them on the map ([5,12,54] for example)
weaverk 1 month ago

Crowdsource coding tasks.

1 Solution


Solution

Hi @weaverk!

I've solved the issue and you can find the project here.

The changes are in WebMapView.js file. (read the comments)

Essentially, the issue was with the wrong usage of useEffect hook. useEffect can be tricky because it functions like componentDidMount and componentDidUpdate in one.

On the page load, when the WebMapView component was mounted useEffect would run and initialize the map as well as the listener that would update the state each time the view changes.
(setImages call in watchUtils.whenTrue)

Each time setImages was called, the state was updated and useEffect block was executed which would cause loading the map and setting the listener again -> making the map unusable

Hopefully, this clears your doubt.
Let me know if you need anything more.

Thank you,
Vladimir

this is great, thank you for the great explanation also!
weaverk 1 month ago
i have a question Vladimir, your suggestion for goal 3 lets me get the value from sidepanel to map component, and i can use it within the map component, but not within the 'map' defined within the original useffect - for a simple example, i would like to be able to run the code 'view.zoom = 12;' where 12 comes from side panel, but this needs to be run where 'view' is accessible in scope, with latest value of '12', do you get my meaning? i will tip if you can solve this completely :)
weaverk 1 month ago
Hi @weaverk. useRef is a perfect solution to this problem. Just like you declared mapRef, you could do the same to store the view object. Since objects are passed by reference in JS, whenever you make a change with mapView.current, the "original" view object is updated and changes are reflected on the map. The example implementation can be found here.
VladimirMikulic 1 month ago
perfect! i just had to wrap it like ' if (mapView.current) {}' because the useffect was firing before it was initialised it seems, all good now, thank you for the excellent explanations also!
weaverk 1 month ago
You're welcome. Thank you for the great tip. Please feel free to contact me if you need any further information/assistance.
VladimirMikulic 1 month ago