问题
The size.value
state is one option behind when the _updateData
function triggers onChange of the <select>
. For example, Let's say the starting defaultValue option of <select>
is "All", locations
is empty...the option is changed to "Medium", locations
is "All"...the option is changed to "Large", locations
is "Medium"...and so on. Initially I want the _updateData
function to run onload with "All" selected which is not working either, it throws the error Cannot read property 'target' of undefined on setSize({value: event.target.value})
. What am I doing wrong here? Thanks for the help.
const Map = () => {
const [viewport, setViewport] = useState({longitude: -98.58, latitude: 39.83, zoom: 3.5})
const [locations, setLocations] = useState([])
const [geojson, setGeojson] = useState(null)
const [size, setSize] = useState({value: "All"})
useEffect(() => {
setLocations(geodata)
_updateData()
}, []);
const _updateViewport = viewport => {
setViewport(viewport)
}
const _updateData = event => {
setSize({value: event.target.value})
const tempLocations = [];
locations.forEach(function(res) {
if (size.value === "All") {
tempLocations.push(res);
} else if (res.Size === size.value) {
tempLocations.push(res);
}
});
var data = {
...
};
setGeojson(data);
}
return (
<ReactMapGL
{...viewport}
onViewportChange={_updateViewport}
width="100%"
height="100%"
mapStyle={mapStyle}
mapboxApiAccessToken={TOKEN}>
<Source id="my-data" type="geojson" data={geojson}>
<Layer {...icon} />
</Source>
<div style={navStyle}>
<NavigationControl onViewportChange={_updateViewport} />
<select onChange={_updateData} defaultValue={size}>
<option value="All">All</option>
<option value="Large">Large</option>
<option value="Medium">Medium</option>
<option value="Small">Small</option>
<option value="Very Small">Very Small</option>
</select>
</div>
</ReactMapGL>
);
}
export default Map;
回答1:
Yes, you are calling your select's onChange
handler in the mounting useEffect
hook with no event object to dereference a target
property. I would factor out the rest of the updateData
code so you can call it with the initial state value. This will allow you to update location details on mount using the initial size state date AND the select's onChange
will remain as it was previously.
NOTE: You should note that updates to state won't take effect until the next render cycle, so in your code you call setSize
with the new value but continue processing locations the current size value, so you need to forward the current value.
const Map = () => {
const [viewport, setViewport] = useState({longitude: -98.58, latitude: 39.83, zoom: 3.5})
const [locations, setLocations] = useState([])
const [geojson, setGeojson] = useState(null)
const [size, setSize] = useState({value: "All"}) // initial size state here
useEffect(() => {
setLocations(geodata);
updateLocationData(size.value); // call the location updater on mount with the initial size state value
}, []);
const _updateViewport = viewport => {
setViewport(viewport)
}
const _updateData = event => {
setSize({value: event.target.value})
updateLocationData(event.target.value); // forward current size value
}
const updateLocationData = (sizeValue) => { // forwarded size value
const tempLocations = [];
locations.forEach(function(res) {
if (sizeValue === "All") { // forwarded size value for comparison
tempLocations.push(res);
} else if (res.Size === sizeValue) { // forwarded size value for comparison
tempLocations.push(res);
}
});
var data = {
...
};
setGeojson(data);
};
return (
<ReactMapGL
{...viewport}
onViewportChange={_updateViewport}
width="100%"
height="100%"
mapStyle={mapStyle}
mapboxApiAccessToken={TOKEN}>
<Source id="my-data" type="geojson" data={geojson}>
<Layer {...icon} />
</Source>
<div style={navStyle}>
<NavigationControl onViewportChange={_updateViewport} />
<select onChange={_updateData} defaultValue={size.value}> // need to unpack the actual size value
<option value="All">All</option>
<option value="Large">Large</option>
<option value="Medium">Medium</option>
<option value="Small">Small</option>
<option value="Very Small">Very Small</option>
</select>
</div>
</ReactMapGL>
);
}
export default Map;
回答2:
First of all, useState function is asynchronous. Therefore takes some time to update. See Is useState synchronous?. And second, the useEffect function is called when the component mounts. So no onchange event has occurred yet. Onchange event occurs once you change the option in select tag. Just remove _updateData from your useEffect function.
来源:https://stackoverflow.com/questions/59550673/react-usestate-option-value-1-step-behind-onchange-selection