I am following the Covid-19 Map tutorial by Coding with Adam. When each country is clicked, the popup displays the country's name along with the infection count. The COVID data can be obtained here.
My replicated example cannot properly display the infection count, returning undefined instead of a number.
const CovidMap = ({ countries }) => {
const mapStyle = {
fillColor:"white",
weight: 1,
color:"black",
fillOpacity: 1,
};
const onEachCountry = (country, layer) =>
{
layer.options.fillColor = country.properties.color;
const name = country.properties.ADMIN;
const confirmedText = country.properties.confirmedText;
layer.bindPopup(`${name} ${confirmedText}`); //confirmedText returns undefined
};
return (
<MapContainer style={{height: "90vh"}} zoom={2} center={[20, 60]}>
<GeoJSON
style={mapStyle}
data={countries}
onEachFeature={onEachCountry} />
</MapContainer>
);
};
I am certain that confirmedText's value can be accessed, because console.log() prints the value. ADMIN and ISO_A3 can be displayed by the popup, but not confirmed nor confirmedText.
UPDATE: Forgot to include the GeoJSON used to build the countries. Here it is.
FURTHER UPDATE: If I add any lines to the code that throw an error, remove the lines, and re-render the page, the popup can return the values. However, subsequent re-renderings would introduce the same problem.
From your SS it doesn't appear that confirmedText is a property of your object. Normally objects show in the terminal like { prop: 'a',... } with the ... standing in for much larger object. I'm also curious why you have to access your sub properties using the properties key word. In a normal object it would just be country.confirmedText. I (me) would start by understanding why you need to access things this way and that might explain why it's undefined.
I'd leave this as a comment but I can't do that yet.
Related
I am trying Create a select field dynamically. When clicking the add button, it will dynamically add select fields. I have and issue where the values don't change.
My code can be viewed on codesandbox:
https://codesandbox.io/s/react-select-field-dynamically-uo4dy?file=/src/App.js
Take a look at my changes:
Forked sandbox
// I added a 3rd argument for the name:
const handleRoomChange = (option, index, name) => {
const value = option.value; // you had this as 'const { value } = option.value' which dereferences value twice
console.log(value);
const list = [...roomInputs];
list[index][name] = value; //you had this as list[index][value]
setRoomInputs(list);
};
// onChange passes the name as 3rd argument to handleRoomChange
<Select
name="boardBasic"
placeHolder="Board"
value={options.value}
onChange={option => handleRoomChange(option, i, "boardBasic")}
options={options}
styles={{
menu: (provided) => ({ ...provided, zIndex: 9999 })
}}
/>
You have three problems that I can see.
First, you only have a handleRoomChange function that is trying to handle both room changes and board changes. You should probably have a handleBoardChange function, as well.
Second, if you output option to the console inside handleRoomChange, you will notice that option.value is a number. So, when you proceed to set list[index][value] to value, what you're saying is that you want (for example) roomInputs[1][1] to be 1. roomInputs[1] has no 1 property, which is why you will end up with the wrong properties inside your object on submission. You need to be setting roomInputs[1].roomType to value, instead (and the boardBasic property in your handleBoardChange method, when you write it).
Third, you are trying to use object destructuring to assign object.value to your value variable...but you're trying to destructure object.value instead of destructuring object. For this reason, value is undefined every time you run the function. Replace the assignment with const { value } = option; and you will start getting defined values back.
i am working on this React-Leaflet Map. I want to update value of "center" prop of MapContainer based on input provided by User, but it is not working.
I know, methods like flyTo(), panTo() are used in such situations but i don't know where/how to apply them... Please help.
Here's the link to codesandbox https://codesandbox.io/s/stoic-lamport-kk8mj?file=/src/App.js
From the official docs:
Except for its children, MapContainer props are immutable: changing
them after they have been set a first time will have no effect on the
Map instance or its container.
As a result, when you change center variable the center does not change. Create a function that changes the map center upon dropdown selection change
function SetViewOnClick({ coords }) {
const map = useMap();
map.setView(coords, map.getZoom());
return null;
}
Include it on MapComp
function MapComp({ coords }) {
return (
<MapContainer
classsName="map"
center={coords}
zoom={4}
scrollWheelZoom={false}
>
...
<SetViewOnClick coords={coords} />
</MapContainer>
);
}
Demo
Note that the coordinates for USA and Canada are not correct so I changed them. They should be
{
USA: [39.7837304, -100.4458825]
},
{
Canada: [61.0666922, -107.9917071]
},
and moreover the countries variable does not have to be a state variable as you do not change it. It should be a constant.
Also there is an error in the console because you are using an array on the select element which expects multi selection when using arrays but obviously you do not want that.
Last but not least you should handle the none selection somehow because an error occurs when selecting none.
As said in the title. I want to create a React component that will give me a possibility to resize its width by dragging - just like windows in Windows operating system. What is actually the best approach to handle this issue ?
EDIT
I included my current approach to the subject of the matter:
First I placed a "dragger" element in the top-right corner of my container. When i press mouse down on that element i want to create a mousemove event listener which will modify the containerWidth in respect to the X coordinate of the cursor relative to the initial X position of the edge of the container. I already have that event listener firing and logging me the coordinates after holding down the mouse button but unfortunatelly for some reason the event is not being removed after the mouse is unpressed(mouseUp event) which is not what i intended. Any suggestions appreciated, also those about some issues i might expect in the future related to this topic. Thanks.
type Props = MasterProps & LinkStateToProps & LinkDispatchToProps;
const Test3 = (Props: Props) => {
const [containerWidth, setContainerWidth] = React.useState(640)
const [isBeingStretched, setIsBeingStretched] = React.useState(false);
const masterRef = React.useRef(null);
const logMousePosition = React.useCallback((event:MouseEvent)=>{
console.log(event.clientX);
},[])
const handleMouseDown=()=>{
document.addEventListener('mousemove', logMousePosition);
masterRef.current.addEventListener('mouseup', ()=>{
document.removeEventListener('mouseup', logMousePosition)
})
}
const handleMouseUp = () => {
document.removeEventListener('mousemove', logMousePosition);
}
return (
<div className="master-wrapper" ref={masterRef}>
<div className="stretchable-div" style={{ width: `${containerWidth}px` }}>
<div className="dragger-wrapper">
<h2>This is supposed to change width</h2>
<div className="dragger"
id="dragger"
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}/>
</div>
</div>
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(Test3);
I'd never done something like this before so I decided to give it a go, and it ended up being quite straightforward to implement with React state management. I can see why you might not know where to start if you are new to React, and that's ok, although two things to note before I go through my solution:
Statements such as document.getElementById or document.addEventListener are not going to function as intended anymore. With React, you are manipulating a virtual DOM, which updates the actual DOM for you, and you should aim to let it do that as much as possible.
Using refs to get around this fact is bad practice. They may act in a similar way to the statements mentioned above but that is not their intended use case. Read up on what the documentation has to say about good use cases for ref.
Here's what the JSX portion of my demo looks like:
return (
<div className="container" onMouseMove={resizeFrame} onMouseUp={stopResize}>
<div className="box" style={boxStyle}>
<button className="dragger" onMouseDown={startResize}>
Size Me
</button>
</div>
</div>
);
We're going to need three different events - onMouseDown, onMouseMove and onMouseUp - to track the different stages of the resize. You already got this far in your own code. In React, we declare all these as attributes of the elements themselves, although they are not actually in-line functions. React adds them as event listeners for us in the background.
const [drag, setDrag] = useState({
active: false,
x: "",
y: ""
});
const startResize = e => {
setDrag({
active: true,
x: e.clientX,
y: e.clientY
});
};
We'll use some state to track the resize as it is in progress. I condensed everything into a single object to avoid bloat and make it more readable, although this won't always be ideal if you have other hooks like useEffect or useMemo dependent on that state. The first event simply saves the initial x and y positions of the user's mouse, and sets active to true for the next event to reference.
const [dims, setDims] = useState({
w: 200,
h: 200
});
const resizeFrame = e => {
const { active, x, y } = drag;
if (active) {
const xDiff = Math.abs(x - e.clientX);
const yDiff = Math.abs(y - e.clientY);
const newW = x > e.clientX ? dims.w - xDiff : dims.w + xDiff;
const newH = y > e.clientY ? dims.h + yDiff : dims.h - yDiff;
setDrag({ ...drag, x: e.clientX, y: e.clientY });
setDims({ w: newW, h: newH });
}
};
The second piece of state will initialise and then update the dimensions of the element as its values change. This could use any measurement you want although it will have to correlate to some CSS property.
The resizeFrame function does the following:
Make the properties of drag easily available via destructuring assignment. This will make the code more readable and easier to type.
Check that the resize is active. onMouseMove will fire for every pixel the mouse moves over the relevant element so we want to make sure it is properly conditioned.
Use Math.abs() to get the difference in value between the current mouse position and the saved mouse position as a positive integer. This will save us from having to do a second round of conditional statements.
Use turnary statements to either add or subtract the difference from the dimensions, based on whether the new mouse position is greater or less than the previous on either axis.
Set the states with the new values, using the spread operator ... to leave the irrelevant part of drag as it is.
const stopResize = e => {
setDrag({ ...drag, active: false });
};
const boxStyle = {
width: `${dims.x}px`,
height: `${dims.y}px`
};
Then we simply set the activity of the drag state to false once the user is finished. The JS style object is passed to the element with the state variable in place so that is taken care of automatically.
Here is the codesandbox with the finished effect.
One of the drawbacks to doing things this way is that it basically requires you to have that mouseMove event listener assigned to the largest site container, because the mouse is not going to stay within the bounds of the box during the resize. That could be an issue if you want to have multiple elements with the same functionality, although nothing that you can't solve with good state management. You could probably fine tune this so that the mouse always stays on the drag element, although that would require a more complex implementation.
I'm building a text editor app in React. My basic structure is
model
class TextLine {
text: string
folded: boolean
indentLevel: number
index: number
model: TextEntryModel
fold: () => this.model.foldLineAtIndex(this.index)
}
class TextModel {
lines: TextLine[]
setText: (text: string) => void // splits the text, creates TextLine objects and sets each line's model to this (line.model = this)
foldLineAtIndex = (index: number) => {
// we're actually folding all the "children" of this line.
const startingLine = this.getLineAtIndex(index);
let previousIndentLevel = startingLine.indentLevel;
// start with the next line
for (let i = index + 1; i < this.lines.length; i++) {
const thisLine = this.lines[i];
if (thisLine.indentLevel > previousIndentLevel) {
thisLine.folded = true;
} else {
return; // if we've reached a line with the same indent level, we're done.
}
}
};
}
basic component structure
<App> // state = { model: new TextEntryModel() };
{ model.lines.map(line =>
<Line
line={line}
onClick={line.fold}
/>
)}
</App>
Something like that, that's a big reduction. When you click the Line's accessory (not in my sketch) it calls fold on the line.
Clicking the line does indeed change the lines in the model, I can confirm this. But the lines don't rerender with their new folded value. At first I just had model as an instance property of the App class, and I thought maybe moving it to the App's state would help, but no dice.
I guess React's question of "have the props changed?" is not affirmative when a property of a property of the state has changed. (I would hope that when model changed it would pass Line a new line prop but maybe model isn't being registered as changing).
So, I could do this with Redux, possibly with a reducer whose type is just { model: TextModel } and use selectors to drill down in each Line, or possibly with a reducer with the simple type TextLine[] (or {lines: TextLine[]}).
But I like the idea of using a real model-ass model. I think this is how I'd do it, correctly, in say Swift or Angular, but it doesn't seem to fit the React paradigm as far as I can tell, which seems weird. So I imagine I'm missing something.
How do I go about this?
I have the following Component that I want to render (
https://github.com/sbycrosz/react-native-credit-card-input/blob/master/src/CreditCardInput.js). Its working fine however I want to assign default card field values e.g. number, using the values props defined.
<CreditCardInput
gooble={{number: 4111111111111111}}
values={{number: 4111111111111111}}
requiresName
requiresCVC
requiresPostalCode
validatePostalCode={validatePostalCode}
cardImageFront={cardFront}
cardImageBack={cardBack}
labelStyle={addCardStyle.label}
inputStyle={addCardStyle.input}
validColor={"black"}
invalidColor={"red"}
placeholderColor={"darkgray"}
onFocus={this._onFocus}
onChange={this._onChange.bind(this)}/>
The actual CreditCardInput.js file should deconstruct these values and pre-populated the fields according to the source code in the link above:
const {
cardImageFront, cardImageBack, inputContainerStyle,
values: { number, expiry, cvc, name, type }, focused,
allowScroll, requiresName, requiresCVC, requiresPostalCode,
cardScale, cardFontFamily, cardBrandIcons,
} = this.props;
<CreditCard
...
number={number}
...
However when i debug on CreditCardInput.js during render I can see that the this.props contains values property, however this is an empty object (instead of containing the number value defined). This seems very odd behaviour. I have tested with other props passed through (E.g: gooble={{number: 4111111111111111}}) and it is populated correctly in this.props even though its exactly the same, except for the names.
So in CreditCardInput.js when I change the source code from above to using the prop gooble it works:
const {
cardImageFront, cardImageBack, inputContainerStyle,
gooble: { number, expiry, cvc, name, type }, focused,
allowScroll, requiresName, requiresCVC, requiresPostalCode,
cardScale, cardFontFamily, cardBrandIcons,
} = this.props;
<CreditCard
...
number={number}
...
However why isnt it working with this.props.values/ why is this.props.values an empty object.