Search code examples
javascriptreactjsreact-hooksreact-state-managementreact-state

How to pass state from hook to display in App hook in React?


How do I send the state(data) of info to the App hook, so that I can display the properties in divs? setInfo(info) stores the data I want to display on the Map hook and I can see all the properties when I console.log(info) in _onClick. I am stuck on how to use and show it in App. Thanks for the help.

App

const App = () => {

  return (
    <div className="App">
      <div className="inner-left map-container">
        <Map />
      </div>
      <div className="inner-right info-container">
        <Nav />
        <Search />
        <div id="info_side">
          {info} // throws an error undefined
        </div>
      </div>
    </div>
  );
}

export default App;

Map

const Map = () => {
    ...
    const [info, setInfo] = useState(null)

    ...

    const _onClick = event => {
        const { features } = event;
        const info = features && features.find(f => f.layer.id === 'icon');
        setInfo(info); // This is what I would like to use in App
    }

    return (
        <ReactMapGL
            {...viewport}
            onViewportChange={_updateViewport}
            width="100%"
            height="100%"
            mapStyle={mapStyle}
            mapboxApiAccessToken={TOKEN}
            onHover={_onHover}
            onClick={_onClick}>

            <Source id="my-data" type="geojson" data={geojson}>
                <Layer {...icon} />
            </Source>

            <div style={navStyle}>
                <NavigationControl onViewportChange={_updateViewport} />
                ...
            </div>
        </ReactMapGL>
    );
}

export default Map;

Solution

  • You can pass a callback prop to the child.

    App

    const App = () => {
    
      const [info, setInfo] = useState(); // local info state
    
      const displayInfo = () => {
        // this is where I would hope to return the <div>'s with data such as <div>info.Company</div>
        // access current copy of info
      }
    
      return (
        <div className="App">
          <div className="inner-left map-container">
            <Map onInfoChange={setInfo} /> // pass info state setter to child
          </div>
          <div className="inner-right info-container">
            <Nav />
            <Search />
            <div id="info_side">
              {displayInfo}
            </div>
          </div>
        </div>
      );
    }
    
    export default App;
    

    Map

    const Map = ({
      onInfoChange, // passed callback prop
    }) => {
        ...
        const [info, setInfo] = useState(null)
    
        // use an effect hook to call callback whenever info updates
        useEffect(() => {
          onInfoChange(info); // pass info back to parent
        }, [info]);
    
        const _onClick = event => {
            const { features } = event;
            const info = features && features.find(f => f.layer.id === 'icon');
            setInfo(info); // This is what I would like to use in App
        }
    
        return (
            <ReactMapGL
                {...viewport}
                onViewportChange={_updateViewport}
                width="100%"
                height="100%"
                mapStyle={mapStyle}
                mapboxApiAccessToken={TOKEN}
                onHover={_onHover}
                onClick={_onClick}>
    
                <Source id="my-data" type="geojson" data={geojson}>
                    <Layer {...icon} />
                </Source>
    
                <div style={navStyle}>
                    <NavigationControl onViewportChange={_updateViewport} />
                    ...
                </div>
            </ReactMapGL>
        );
    }
    
    export default Map;
    

    At this point I should point out that you now have duplicate state stored in two components so it's a great idea to hoist the info state and logic to the closest common ancestor (App) and simply pass the info as a prop to the component that needs it (Map). Single source of truth principle. Or as other have pointed out, switch to a global state management system, like redux, or roll your own using React Context (i.e. what react-redux is using under the hood).