Best way of sending request to child component - javascript

I am creating a map application that consists of a map (react-map-gl) showing GeoJSON markers and a simple table that lists the properties of the same markers.
Both the map component and the table component are direct children of a common component (the main App component). When clicking a marker in the table, I want the map component to zoom to this marker.
For the map to start a transition, I need to set its state.viewport like shown in this example:
https://visgl.github.io/react-map-gl/docs/advanced/viewport-transition
If you move the "New York City" button in that example to a sister component instead, you have my case exactly.
How do I send a map transition request from a sister component to the map component in the "React way"?

The React way is to have a common parent that will handle all state related actions, that is called lifting your state up. The idea is to always keep a single source of truth, let it go down the tree via props and use function to update the state.
In your case, a single exemple could look like this:
const defaultViewport = viewport: {
width: 800,
height: 600,
longitude: -122.45,
latitude: 37.78,
zoom: 14,
}
// The app will handle the viewport and related update
const App = () => {
const [viewport, setViewport]= useState(defaultViewport);
const handleTransition = (transitionViewport) => {
setViewport({
...viewport,
...transitionViewport
});
}
const handleViewportChange = (newViewport) => {
setViewport(newViewport);
}
return (
<div>
<Map
viewport={viewport}
onViewportChange={handleViewportChange}
/>
<Menu
onTransition={handleTransition}
/>
</div>
);
}
// The map only need to be aware of the viewport and how to change it
const Map = ({ viewport, onViewportChange }) => {
return (
<ReactMapGL
{...viewport}
onViewportChange={onViewportChange}
/>
);
}
// The menu only need to know how to request a transition
const Menu = ({onTransition}) => {
const goToNYC = () => {
const viewport = {
longitude: -74.1,
latitude: 40.7,
zoom: 14,
transitionDuration: 5000,
transitionInterpolator: new FlyToInterpolator(),
transitionEasing: d3.easeCubic
};
onTransition(viewport);
}
return (
<div>
<button onClick={goToNYC}>New York City</button>
<div>
);
}

You'll need a parent component that will handle the state changes and then send the state and handler as props to the child components.
For example below, you have a parent component called MyApp which has two child components, Map which contains the map and NYCButton which contains the button that triggers the state change.
The MyApp component...
import React, { useState } from "react";
import { FlyToInterpolator } from "react-map-gl";
import d3 from "d3-ease";
import { Map } from "./Map";
import { NYCButton } from "./NYCButton";
const MyApp = () => {
const [viewport, setViewport] = useState({
width: 800,
height: 600,
longitude: -122.45,
latitude: 37.78,
zoom: 14
});
const goToNYC = () => {
setViewport({
...viewport,
longitude: -74.1,
latitude: 40.7,
zoom: 14,
transitionDuration: 5000,
transitionInterpolator: new FlyToInterpolator(),
transitionEasing: d3.easeCubic
});
};
return (
<div className="MyApp">
<Map viewport={viewport} onViewportChange={setViewport} />
<NYCButton goToNYC={goToNYC} />
</div>
);
};
export { MyApp };
The Map component...
mport React from "react";
import ReactMapGL from "react-map-gl";
const Map = ({ viewport, onViewportChange }) => (
<div>
<ReactMapGL {...viewport} onViewportChange={onViewportChange} />
</div>
);
export { Map };
The NYCButton component...
import React from "react";
const NYCButton = ({ goToNYC }) => (
<div>
<button onClick={goToNYC}>New York City</button>
</div>
);
export { NYCButton };

Related

How do I make an infowindow appear when the user clicks the marker, while using a functional component?

The Google Maps API library I am using for this is https://github.com/JustFly1984/react-google-maps-api
What I have been asked to create is a map with multiple markers, each of which can be clicked on to open an InfoWindow centred on that marker. Much like the AirBnB search page, with the map on one side results on the other.
However, I seem to only be able to have an InfoWindow that is open at the loading of the page and then can't be opened again.
My research so far has generally shown that I would need to completely redo this component as a class instead. Although I don't really understand the class based model of components, so ideally I wouldn't have to mess around with that.
Is there a way to make the desired effect without reworking my program entirely?
This is the code for the component, in case it is relevant
import styles from '../../styles/Home.module.css';
import { Marker, LoadScriptNext, GoogleMap, InfoWindow } from '#react-google-maps/api';
import { Fragment, React } from 'react';
import { Typography } from '#material-ui/core';
export default function MapPanel(props) {
return (
<Fragment>
<LoadScriptNext id='google-map-example' googleMapsApiKey="MYAPIKEY">
<GoogleMap mapContainerStyle={{ width: "100%", height: "800px" }}
id='google-map'
center={props.centre}
heading="0"
zoom={12}>
{addMarkers(props.markerList)}
</GoogleMap>
</LoadScriptNext>
</Fragment>
);
}
function addMarkers(markerList) {
if (markerList != undefined) {
return (markerList.map((marker, index) => makeMarker(marker, index)))
}
}
function makeMarker(marker, index) {
const handleClick = () => {
console.log(`${marker.title} marker clicked`);
}
return (
<Marker id={index} position={marker.latLong} title={marker.title} onClick={handleClick}>
<InfoWindow id='Test' position={marker.latLong}>
<Typography variant='body2'>{marker.text}</Typography>
</InfoWindow>
</Marker>
);
}
To achieve your goal you would want to use states in your application. But since you want to deal with it using Function components, you would need to make use of react hooks to use states.
Here is a working sample for your reference: https://stackblitz.com/edit/marker-with-infowindow-react-api-o6357y
import ReactDOM from "react-dom";
import React from "react";
import {
GoogleMap,
Marker,
InfoWindow,
LoadScript
} from "#react-google-maps/api";
import data from "./data.json";
const { useState } = React;
const containerStyle = {
width: "400px",
height: "400px"
};
const center = { lat: 40.712775, lng: -74.005973 };
function Map() {
const [infoWindowID, setInfoWindowID] = useState("");
let markers;
if (data !== null) {
markers = data.map((location, i) => {
const marker = { lat: location.lat, lng: location.lng };
const index = i + 1;
return (
<Marker
key={index}
position={marker}
label={index.toString()}
onClick={() => {
setInfoWindowID(index);
}}
>
{infoWindowID === index && (
<InfoWindow>
<span>Something {index}</span>
</InfoWindow>
)}
</Marker>
);
});
}
return (
<LoadScript googleMapsApiKey="YOUR_API_KEY">
<GoogleMap mapContainerStyle={containerStyle} center={center} zoom={10}>
{markers}
</GoogleMap>
</LoadScript>
);
}
export default React.memo(Map);

Leaflet and React (Map container not found) On Render

I have a React and Leaflet.js component...
export const MapSetLocationComponent = (props) => {
React.useEffect(() => {
let marker = null
let map = null
var container = L.DomUtil.get('setLocationMap')
if (container != null) {
container._leaflet_id = null
}
map = L.map('setLocationMap', {
center: [51.4556852, -0.9904706],
zoom: 16,
layers: [L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {})]
})
map.on('click', function (e) {
if (marker) map.removeLayer(marker)
marker = L.marker([e.latlng.lat, e.latlng.lng], { title: 'My marker' }).addTo(map)
})
})
return (
<>
<h3>Place your pin</h3>
<div
id="setLocationMap"
style={{
width: '100%',
height: '300px'
}}
/>
</>
)
}
Sometimes when I load the parent component I get the following error
'Map container not found.'
Which would indicate that the is somehow not available, because if I comment out that div I get the same error.
Anybody got any ideas? Do I need to use a different hook?
Indeed you should use the useRef hook to access the actual DOM element (once it is available) and pass it to Leaflet L.map factory.
function myComponent(props) {
const mapRef = useRef(null);
useEffect(() => {
if (mapRef && mapRef.current) {
L.map(mapRef.current); // ref.current is the actual DOM element
}
}, [mapRef]); // re-evaluate once the ref is available
render (<div ref={mapRef} />)
}
That is how React Leaflet, mentionned by #pilchard, works under the hood (simplified).
As pointed out by #MattMorgan, your rendered template may not be inserted in the actual DOM tree initially, hence your DOM query may fail. As a general rule, refrain from directly fiddling with DOM that React is supposed to manage.

How to pass coordinates data from a class to another class in React

I want to pass coordinates from a class to another one. I am retrieving coordinates from an api. The code looks like the following:
import React, { Component } from 'react';
class Test3 extends Component{
state = {
loading: true,
coordinates: null,
}
async componentDidMount(){
const url = "https://ttr.vsbbn.nl:4000/gps_history?team_id=10";
const response = await fetch(url);
const data = await response.json();
this.setState({coordinates: data, loading: false });
}
render(){
const { loading, coordinates } = this.state
return(
<div>
{loading || !coordinates ? (
<div>loading...</div>
) : (
<div>
{coordinates.map((coordinate, index) => {
return (
<div key={index}>
<p>Longitute: {coordinate.lon}</p>
<p>Latitude: {coordinate.lat}</p>
<p>Time: {coordinate.timestamp}</p>
<p>...............</p>
</div>
)
})}
</div>
)}
</div>
)
}
}
export default Test3;
And i want to pass the coordinates to the class here below. I already have the path hardcoded in an array. The api coordinates supposed to be in the path array, thats my goal. The code is here:
import React from 'react';
import {
GoogleMap,
withScriptjs,
withGoogleMap,
Marker,
Polyline}
from 'react-google-maps';
import '../App.css';
import { Link } from "react-router-dom";
class Map extends React.Component {
path = [
{ lat: 18.558908, lng: -68.389916 },
{ lat: 18.558853, lng: -68.389922 },
{ lat: 18.558375, lng: -68.389729 },
{ lat: 18.558032, lng: -68.389182 },
{ lat: 18.55805, lng: -68.388613 },
{ lat: 18.558256, lng: -68.388213 },
{ lat: 18.558744, lng: -68.387929 }
];
render = () => {
return (
<GoogleMap
defaultZoom={16}
defaultCenter={{ lat: 18.559008, lng: -68.388881 }}
>
<Polyline path={this.path} options={{ strokeColor: "#FF0000 " }} />
<Marker position={this.path[this.path.length - 1]} />
</GoogleMap>
)
}
}
const WrappedMap = withScriptjs(withGoogleMap(Map));
export default() => (
<div>
<br/>
<b>Klik <Link to="/" className="btn btn-primary">hier</Link> om terug te gaan naar teamoverzicht</b>
<br/>
<br/>
<h3>Live kaart</h3>
<p>
Teamnaam: Hogeschool Rotterdam 1 <br/>
Live:<span class="dot"></span><br/>
Actuele snelheid: 8 km per uur <br/>
Gemiddelde snelheid: 7 km per uur <br/>
Verwachte aankomsttijd Rotterdam: 12 mei 17.59 <br/>
</p>
<div style={{width: '50vw', height: '50vh'}}>
<WrappedMap googleMapURL = {'https://maps.googleapis.com/maps/api/js?key=AIzaSyB9w0T_VMezCy1AaqxXpRie9ChrbVCt1O4&v=3.exp&libraries=geometry,drawing,places'}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100%` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
</div>
</div>
)
Any idea how to do this? My goal is showing real time the location of a person.
Data passing in React is unidirectional, it means that only a parent can pass props to its child. So if you can nest your component "Map" into "Test3" that will solve it. If you can't do that, then you need to use Redux, which is state management library, that will handle complex data passing between components.
PS: there is way to pass data from a child to its parent, but its very limited (I can send you a tuto if you're interested)
According to this documentation, A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API, per se. They are a pattern that emerges from React’s compositional nature
so after you read this doc we are here to answer all of your questions about it.
you need to save the coordinates in an ordered way (to your state) that you could pass it to the children components (in your case <Map />).
in the componentDidMount of your <Test3 /> component try this:
async componentDidMount(){
const url = "https://ttr.vsbbn.nl:4000/gps_history?team_id=10";
const response = await fetch(url);
const data = await response.json();
const coordinatesList = []
data.forEach(coor => {
coordinatesList.push({ lat: coor.lat, lng: coor.lng })
})
this.setState({coordinates: coordinatesList, loading: false });
}
this would result something like below:
coordinatesList: [
{ lat: someNumber, lng: someNumber },
{ lat: someNumber, lng: someNumber },
{ lat: someNumber, lng: someNumber },
...
]
after that inside of render function you can pass the coordinatesList state to your child component and use it, something like
<Map coorList={coordinatesList} />
no need to loop over coordinates! you need to do what ever you want inside the map component wityh all of your coordinates. (as a side note i'm thinking that you try to create markers of those coordinates, so you need to loop over coorList props that you've been received from your parent and return a <Marker /> component of each of that coordinates )
Another option could be use use react context/provider, which would serve as a container for your data and also any functions to update the data. Your context could be accessed from both components and gives you the same single source of data that redux gives you, without all the boilerplate code that goes with redux.
Create a provider component and a use context hook
import React, { createContext, useContext, useState, useEffect } from 'react'
const Context = createContext({})
// This is your provider
const MapDataProvider = props => {
const [mapData, setMapData] = useState([...your original coordinates])
useEffect(() => {
...your call to google
.then(data, setMapData(current => [...current, data])
}, [])
return (
<Context.Provider value={{coordinates: mapData}}>
{props.children}
</Context.Provider>
)
}
// This is your hook
const useMapDataContext = () => useContext(Context)
export { MapDataProvider, useMapDataContext }
Wrap the exported Provider around your root component
import { MapDataProvider } from './yourcontextfile.js'
const RootComponent = () => (
<MapDataProvider>
<Test3/>
<WrappedMap/>
...whatever, the structure here is not important
</MapDataProvider>
)
Then in any of your components where you need to access the data, you use the exported hook useMapDataContext
import { useMapDataContext } from './yourcontextfile.js'
const Test3 = () => {
// This is deconstructed from the value prop on the provider component
const { coordinates } = useMapDataContext()
... the rest of your component
}
I haven't compiled or tested any of this, it's just a quick example of how you could use a context component to have your data in one place, accessible from any other component, as long as it's inside the <MapDataProvider> component.
I'm not saying it's the right way to do it, it's just another way.

Slow Performance for filtering markers in react-leaflet

I need an advice for the react-leaftlet port of leaflet. I am generating markers on a map and use marker clustering with react-leaflet-markercluster. Each markerdata is associated with some data. I want to filter that data based on the markers in the viewport.
My idea: Get the boundaries of the map and cross-check with each markers. Yes, it works. But the performance is extremly slow (> 4.5secs for calculating), when adding more than 500 markers.
What can I do to increase the performance?
Here is my code:
import React, { Component, Fragment } from 'react';
import CustomMarkers from './components/CustomMarkers';
import { Map, TileLayer } from 'react-leaflet';
import ImageContainer from './components/ImageContainer';
import { checkIfMarkerOnMap, createSampleData } from './utils/helpers';
import L from 'leaflet';
class App extends Component {
constructor(props){
super(props);
this.state = {
viewport: {
width: '100%',
height: '400px',
latitude: 40.00,
longitude: 20.00,
zoom: 5
},
visibleMarkers: {},
markers : {},
}
}
componentDidMount = () => {
const sampleData = createSampleData(1000);
this.setState({ markers: sampleData, visibleMarkers: sampleData });
const mapBoundaries = this.mapRef.contextValue.map.getBounds();
this.setState({ mapBoundaries });
}
getMapBoundaries = () => {
// Get map boundaries
const mapBoundaries = this.mapRef.contextValue.map.getBounds();
if(this.state.mapBoundaries !== mapBoundaries){
console.log("different");
this.setState({ mapBoundaries } );
} else return;
}
checkVisibleMarkers = () => {
console.time("checkVisibleMarkers");
const { markers, mapBoundaries } = this.state;
let visibleMarkers = Object.keys(markers)
.filter(key => (L.latLngBounds([[mapBoundaries._southWest.lat, mapBoundaries._southWest.lng], [mapBoundaries._northEast.lat, mapBoundaries._northEast.lng]]).contains([markers[key].coordinates.latitude,markers[key].coordinates.longitude])))
.map(key => { return { [key] : markers[key] } });
visibleMarkers = Object.assign({}, ...visibleMarkers);
console.log("visibleMarkers", visibleMarkers);
// this.setState({ visibleMarkers })
console.timeEnd("checkVisibleMarkers");
}
handleViewportChanged = () => {
this.getMapBoundaries();
this.checkVisibleMarkers();
}
render() {
console.log("this.mapRef", this.mapRef);
const { viewport, markers, visibleMarkers } = this.state;
const position = [viewport.latitude, viewport.longitude]
return (
<Fragment>
<Map
ref={(ref) => { this.mapRef = ref }}
center={position}
zoom={viewport.zoom}
maxZoom={15}
onViewportChanged={() => this.handleViewportChanged()}
style={{ height: '400px' }}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution="© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors"
/>
<CustomMarkers visibleMarkers={markers} />
</Map>
{/* <ImageContainer visibleMarkers={visibleMarkers} /> */}
</Fragment>
)
}
}
export default App;
CustomMarker.js:
import React, { Component } from 'react';
import { Marker, Tooltip } from 'react-leaflet';
import uuid from 'uuid-v4';
import {
heartIcon,
heartIconYellow,
heartIconLightblue,
heartIconDarkblue } from './../icons/icons';
import MarkerClusterGroup from 'react-leaflet-markercluster';
import L from 'leaflet';
class CustomMarkers extends Component {
render() {
const { visibleMarkers } = this.props;
let markers;
if(Object.keys(visibleMarkers).length > 0) {
markers = Object.keys(visibleMarkers).map(key => {
let latitude = visibleMarkers[key].coordinates.latitude;
let longitude = visibleMarkers[key].coordinates.longitude;
let icon = heartIcon;
if(visibleMarkers[key].category === 'fb') icon = heartIconLightblue;
if(visibleMarkers[key].category === 'blogs') icon = heartIconYellow;
if(visibleMarkers[key].category === 'artisan') icon = heartIcon;
if(visibleMarkers[key].category === 'website') icon = heartIconDarkblue;
return (
<Marker
key={uuid()}
position={ [latitude, longitude] }
icon={icon}
>
<Tooltip>{visibleMarkers[key].category}</Tooltip>
</Marker>
)
});
}
const createClusterCustomIcon = (cluster) => {
return L.divIcon({
html: `<span>${cluster.getChildCount()}</span>`,
className: 'marker-cluster-custom',
iconSize: L.point(40, 40, true),
});
}
return (
<MarkerClusterGroup
iconCreateFunction={createClusterCustomIcon}
disableClusteringAtZoom={10}
zoomToBoundsOnClick={true}
spiderfyOnMaxZoom={false}
removeOutsideVisibleBounds={true}
maxClusterRadius={150}
showCoverageOnHover={false}
>
{markers}
</MarkerClusterGroup>
)
}
}
export default CustomMarkers;
createSampleData takes the amount of sample data to generate as an input and creates a json structure for sample data { id: 1 { coordinates: {},...}
The bottleneck is the function checkVisibleMarkers. This function calculates if the marker is in viewport. Mathimatically its just two multiplications per marker.
I see a few potential issues - the performance of the checkVisibleMarkers function, and the use of uuid() to create unique (and different) key values on each rerender of a <Marker />.
checkVisibleMarkers
Regarding the checkVisibleMarkers function. There's a few calls and patterns in there which could be optimized. Here's what's currently happening:
Create an array of the markers keys
Loop through the keys, reference the corresponding marker and filter by location using L.latLngBounds().contains()
Loop through the filtered keys to create an array of objects as {key: marker}
Use Object.assign() to create an object from the array of objects
In the end, we have an object with each value being a marker.
I'm unsure of the internals of L.latLngBounds but it could be partly responsible for the bottleneck. Ignoring that, I'll focus on refactoring the Object.assign({}, ...Object.keys().filter().map()) pattern using a for...in statement.
checkVisibleMarkers = () => {
const visibleMarkers = {};
const { markers, mapBoundaries } = this.state;
for (let key in markers) {
const marker = markers[key];
const { latitude, longitude } = marker.coordinates;
const isVisible = mapBoundaries.contains([latitude, longitude]);
if (isVisible) {
visibleMarkers[key] = marker;
}
}
this.setState({ visibleMarkers });
}
A quick check on jsPerf shows the above method is ~50% faster than the method you're using, but it doesn't contain the L.latLngBounds().contains() call so it's not an exact comparison.
I also tried a method using Object.entries(markers).forEach(), which was slightly slower than the for...in method above.
The key prop of <Marker />
In the <Marker /> component you're using uuid() to generate unique keys. While unique, each rerender is generating a new key, and every time a component's key changes, React will create a new component instance. This means every <Marker /> is being recreated on every rerender.
The solution is to use a unique, and permanent, key for each <Marker />. Thankfully it sounds like you already have a value that will work for this, the key of visibleMarkers. So use this instead:
<Marker
key={key}
position={ [latitude, longitude] }
icon={icon}
>
<Tooltip>{visibleMarkers[key].category}</Tooltip>
</Marker>

How to pass values from on component to Redux form

I am using Redux form for form in React.js and my form was and I have a custom google map component I want to bind lat and long to my form
form
import React from 'react';
import { Field, reduxForm } from 'redux-form';
const SimpleForm = props => {
const { handleSubmit, pristine, reset, submitting } = props;
return (
<form onSubmit={handleSubmit}>
<div className="position-relative form-group">
<label>First Name</label>
<div>
<Field
name="firstName"
component="input"
type="text"
placeholder="First Name"
className="form-control"
/>
</div>
</div>
<Field name = 'eventLocation'
component = {MyParentComponentWrapper} />
</form>
);
};
export default reduxForm({
form: 'simple', // a unique identifier for this form
})(SimpleForm);
and my MyParentComponentWrapper code was
import React from 'react';
import { compose, withProps, lifecycle } from 'recompose';
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from 'react-google-maps';
const MyMapComponent = compose(
withProps({
googleMapURL:
'https://maps.googleapis.com/maps/api/js?key=AIzaSyCYSleVFeEf3RR8NlBy2_PzHECzPFFdEP0&libraries=geometry,drawing,places',
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
lifecycle({
componentWillMount() {
const refs = {};
this.setState({
position: null,
onMarkerMounted: ref => {
refs.marker = ref;
},
onPositionChanged: () => {
const position = refs.marker.getPosition();
console.log(position.toString());
},
});
},
}),
withScriptjs,
withGoogleMap
)(props => (
<GoogleMap defaultZoom={8} defaultCenter={{ lat: -34.397, lng: 150.644 }}>
{props.isMarkerShown && (
<Marker
position={{ lat: -34.397, lng: 150.644 }}
draggable={true}
ref={props.onMarkerMounted}
onPositionChanged={props.onPositionChanged}
/>
)}
</GoogleMap>
));
class MyParentComponentWrapper extends React.PureComponent {
state = {
isMarkerShown: false,
};
render() {
return (
<div>
<MyMapComponent isMarkerShown={true} />
</div>
);
}
}
export default MyParentComponentWrapper;
this component will console.log the lat and long values when user drag the marker
How to pass the console.log value to redux-form?
Can anyone please suggest a way to do so?
Here is a codesandbox of your app using a redux-form. Notice that after setting up the form in latLngForm.js, I use connect in your map container to dispatch reduxForm's change action when your marker is moved. This is what updates the store.
I also pass position in as a prop to <MyMapComponent /> to set the position of your marker. This means that your marker's position is always based off of the form's values, and that moving the map's markers manually changes the form's value. This will allow you to set the position manually via the fields, or by dragging and dropping the marker.
mapStateToProps in the <MyMapComponent /> is the important piece here. redux-form automatically stores the values in the state for us, this is where we retrieve it.
Notice these lines at the top of the file:
import { change, formValueSelector } from "redux-form";
...
const formSelector = formValueSelector("form");
This sets up our form selector. "form" being the identifier for the form. Now, to retrieve these values from our state, we do:
const mapStateToProps = (state) => ({
position: {
lat: formSelector(state, 'lat'),
lng: formSelector(state, 'lng') // The key here is the name you passed into the field.
}
});
Then we use connect to actually connect the component to the store:
connect(mapStateToProps)(MyMapComponent);
Redux does its magic and now our fields are available via this.props.position in our component!
Here is my working solution, forked from your sandbox.
I just added a callback in your MyParentComponentWrapper component which is fired every time the marker position changes. In your form, you listen to the callback and set the position field with the value received from MyParentComponentWrapper.
I like to keep components as simpler as possible and not to connect them with redux state via connect. Just think of your MyParentComponentWrapper as an input field which only fires change event every time its value changes without being aware of how/where this value will be used.
In this case MyParentComponentWrapper just show a marker and invokes a callback every time marker position changes. It's the form component the one in charge of handling this position in the proper way.
Here's the how I changed your form render with the callback:
<MyParentComponentWrapper setPosition={onPositionSet} />
and here's the callback in your form component:
const onPositionSet = (position) => {
props.change("position", position);
}
where change function has been added to your component props as you wrapped it in a redux-form
For functional components you can do this:
useEffect(()=>{
dispatch(change("travellerDetailsForm", "dob", "DD/MM/YYYY"));
},[])
or you can do it in the change handler with the event too like this-
const panNameChangeHandler = (e) => {
e.preventDefault();
const panName = e.target.value.toUpperCase();
dispatch(change("travellerDetailsForm", "name", panName));
};

Categories