React-leaflet problem to create <Marker> <Popup> instance dynamic - dynamic

I would like to create a react-leaflet object dynamically. Normal leaflet objects work, but react-leaflet do not.
Code example on:
https://codesandbox.io/s/show-problem-react-leaflet-marker-popup-object-hm0o8?file=/src/MapView.js
Left click shows the desired behavior with leaflet objects and right click the problem with react-leaflet.
problem code:
var lat = e.latlng.lat;
var lon = e.latlng.lng;
// create popup contents
// use <Marker> from react-leaflet
var newMarker = <Marker position={[lat, lon]}>
{/* use Edit Popup */}
<Popup editable open>
Your stylable content here.
</Popup>
</Marker>;
// Problem addTo(map)
newMarker.addTo(map);

newMarker is a react element. You can't call the leaflet method addTo on it, because it is not a leaflet L.Marker instance.
If you want to be able to manage the markers through react, you would need to keep a state variable which is an array of coordinates. On map click, you can add the coordinate to that array, and then render a series of <Marker /> elements from that.
In your event handlers, you're simply going to capture the position of the click and pass it to a callback setMarkers:
function MyRightClickEventHandler({ setMarkers }) {
useMapEvents({
contextmenu: (e) => {
setMarkers(e.latlng);
}
});
return null;
}
setMarkers is a callback to setState on your primary MapView component, which adds the latlng to the state variable, which contains an array of latlngs:
// inside you'r MapContainer:
<MyRightClickEventHandler
setMarkers={(markers) =>
this.setState({
MarkerArray: [...this.state.MarkerArray, markers]
})
}
/>
Then map over the latlngs that are in that state variable, if there are any:
{this.state.MarkerArray &&
this.state.MarkerArray.map((latlng) => (
<Marker position={latlng}>
<Popup editable removable>
Thanks for using my editable popup plugin!
</Popup>
</Marker>
))}
Working codesandbox
Note that if you're going to use the editable, removable, or open props on editable popups rendered dynamically from an array, be sure to read my section on using editable popups rendered from a dynamic state array - it can get hairy if you're not careful. Feel free to ask questions in the comments if you run into problems.

Related

Struggling with useEffect and flatlist

I am rendering a component for every item in a flatList. Each component has a label, and when the component is rendered, I have a useEffect that fetches the updated label name for that specific label.
For some reason, it seems to only be running for the last item in the flatList. The last item is the only item with the updated name, while all other still contain the outdated information.
Assuming there is an updated name for each label, why could my useEffect only be running on the last item?
<FlatList
data={labels}
keyExtractor={keyExtractor}
renderItem={renderItem}
/>
Label.js - I would think this would run for every label component rendered. Could there be a possible issue with what I have here? Or must it be somewhere else in my code?
let name = label.name;
useEffect(() => {
updateLabel()
name = label.name
}, [label]);
return (
<>
{name}
</>
)
I see several possible issues. Some important code is missing, so I'll answer what I can.
You're not using state to hold your label name in the Label component (name = label.name), so React will never know to re-render the component when it changes. It's rare to need to use a let variable in React. To hold properties that the component needs to change, use the useState hook.
However, you shouldn't do that here, because of the next point.
It looks like you are updating the label somewhere else, and also locally (name = label.name). Don't do this, it's too easy for the two to get out of sync and cause bugs. If the name is coming from somewhere else, show it and set it from props.
I'm not sure what updateLabel() does or where it comes from (how does the function know what to update the label to?), but if you need it, it should come from props.
If label.name is a string, you can't render it in a fragment. You must render it in a Text component. <Text>{label.name}</Text>
The object that FlatList passes in to the renderItem callback does not have a property called label, you are looking for item - this is the object from the data prop.
function renderLabel({ item }) { // item, not label
return <Label label={item} onPress={() => onPressLead(item)}/>;
}
const Label = ({ label, updateLabel }) => {
// no local label variable
useEffect(() => {
updateLabel(); // what is this supposed to do?
}, []); // no dependencies, if you only want to update the label once on mount
return <Text>{label.name}</Text>; // if label.name is a string
};
// your FlatList is fine as written
Your use effect probably needs the label as a dependency.
useEffect(() => {
updateLabelName()
}, [label]);

Does anyone know how to create this component in React native?

I want to create a component as seen in the image attached. The circle is a progress bar and the blue colour is the progress, I could use a circular progress bar component for that but I'm not sure how can I show dates as seen in the image, the text in blue will be the current date. If anyone knows how to do this please help.
Edit: I want to add the date to the progress bar, I have found the circular progress bar component.
This is called Circular Progress
Usage
import { AnimatedCircularProgress } from 'react-native-circular-progress';
<AnimatedCircularProgress
size={120}
width={15}
fill={100}
tintColor="#00e0ff"
onAnimationComplete={() => console.log('onAnimationComplete')}
backgroundColor="#3d5875" />
You can also define a function that'll receive current progress and for example display it inside the circle:
<AnimatedCircularProgress
size={200}
width={3}
fill={this.state.fill}
tintColor="#00e0ff"
backgroundColor="#3d5875">
{
(fill) => (
<Text>
{ this.state.fill }
</Text>
)
}
</AnimatedCircularProgress>
You can also define a function that'll receive the location at the top of the progress circle and render a custom SVG element:
<AnimatedCircularProgress
size={120}
width={15}
fill={100}
tintColor="#00e0ff"
backgroundColor="#3d5875"
padding={10}
renderCap={({ center }) => <Circle cx={center.x} cy={center.y} r="10" fill="blue" />}
/>
Finally, you can manually trigger a duration-based timing animation by putting a ref on the component and calling the animate(toValue, duration, easing) function like so:
<AnimatedCircularProgress
ref={(ref) => this.circularProgress = ref}
...
/>
this.circularProgress.animate(100, 8000, Easing.quad); // Will fill the progress bar linearly in 8 seconds
The animate-function returns the timing animation so you can chain, run in parallel etc.
This is what you are looking for...
https://github.com/JesperLekland/react-native-svg-charts
It is ProgressCircle. hope help You...

How can I detect press on background?

Is it possible to detect press on background (not on items inside FlatList) of FlatList? How?
What I tried:
onPress prop did not work (as expected since it is not documented).
Wrapping FlatList inside TouchableHighlight element did not trigger.
If your Flatlist is inside a View you can use this method:
<View
onStartShouldSetResponder={(evt) => true}
onResponderGrant={(evt) => {
this.handlePress(evt.nativeEvent)
}}
>
Then you have to handle the differents events.
the event object contains many variables you can use.
exemple:
handlePress(event){
console.log('x = ' + event.pageX)
console.log('y = ' + event.pageY)
}
doc: https://facebook.github.io/react-native/docs/gesture-responder-system
I don't think that it is possible as there is no method that could be used for it connected to FlatList. A full list of the methods can be found here: https://facebook.github.io/react-native/docs/flatlist#listemptycomponent
However, if you need to add some functionality to your flatlist that is not connected to it's items the onRefresh function does a great job. It is triggered when you "overscroll" the list and works for flatlist with regular orientation as well as reversed!
Paste this into your flatlist and write the method:
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh}
/>}

Using FlatList#onViewableItemsChanged to call a Component function

I'm currently attempting to implement a form of LazyLoading using the FlatList component, which introduces a neat little feature called onViewableItemsChanged which gives you a list of all of the components that are no longer on the screen as well as items that are now on the screen.
This is a custom LazyLoad implementation and as such is more complicated than most LazyLoad open-sourced libraries that are available, which is why I'm working on my own implementation. I'm already looked into react-native-lazy-load and others.
Basically, I need to be able to call a function that's part of the component being rendered in the FlatList, I've tried creating a reference to the item rendered in the FlatList and calling it as such, but it doesn't seem to work.
For example:
<FlatList data={...}
renderItem={(item) => <Example ref={(ref) => this[`swiperRef_${item.key}`] = ref}}
onViewableItemsChanged={this.onViewableItemsChanged}
/>
onViewableItemsChanged = ({viewableItems}) => {
viewableItems.forEach((item) => {
const { isViewable, key } = item;
if(isViewable && !this.cachedKeys.includes(key)) {
const ref = this[`swiperRef_${key}`];
if(!ref) return console.error('Ref not found');
ref.startLoading();
this.cachedKeys.push(key);
}
});
}
Now in the <Example /> component I would have a function called startLoading which should be called when a new visible item is brought onto the screen, however the ref never exists.
I was actually doing everything correctly, but I accidently forgot to deconstruct the parameter returned from the renderItem function, so (item) should have been ({ item })
That's all there was to it.

Set slider to initial position - React native

I have an app in which there is a common slider to display 2 different data values. And a button which controls the values to show (i.e. To use Array1 Or Array2).
Slider is displaying all values properly while doing back-n-forth in slider.
When I click the button, I update the state, Render gets called with correct Array to load from.
Issue: Slider starts from some random position. I want slider head to go to initial position.
Below is my slider code,
<Slider style={styleSliderView.slider}
minimumValue={0}
maximumValue={arr.length-1}
step={1}
onValueChange={(value)=>this._onValueChange(value)}/>
Any thoughts.
This is probably not just a random position. It's the new position at the new min/max range. What you need to do is somehow reset the slider.
You can try 2 possible solutions:
1) save the slider value in your state and reset it when the button is pressed:
<Slider
value={this.state.value}
onValueChange={(value) => this.setState({value})}
/>
onButtonPress() {
this.setState({value: 0});
}
2) reset the value using a ref to the slider:
<Slider
ref={r => this.slider = r}
/>
onButtonPress() {
this.slider.setNativeProps({value: 0});
}