反应 useState 选项值落后于 onChange 选择 1 步
React useState option value 1 step behind onChange selection
size.value
状态是_updateData
函数触发<select>
的onChange时的一个选项。例如,假设 <select>
的起始默认值选项为 "All",locations
为空...选项更改为 "Medium",locations
为 "All"...选项改为"Large",locations
为"Medium"...等。最初我希望 _updateData
函数在选择 "All" 的情况下 运行 加载,但它也不起作用,它抛出错误 Cannot read 属性 'target' of undefined on setSize({value: event.target.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"})
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;
首先useState函数是异步的。因此需要一些时间来更新。参见 Is useState synchronous?。
其次,当组件挂载时调用 useEffect 函数。所以还没有发生 onchange 事件。一旦您更改 select 标记中的选项,就会发生 onchange 事件。
只需从您的 useEffect 函数中删除 _updateData。
是的,您在安装 useEffect
挂钩中调用 select 的 onChange
处理程序,没有事件对象来取消引用 target
属性 .我会分解出 updateData
代码的其余部分,以便您可以使用初始状态值调用它。这将允许您使用初始大小状态日期 AND select 的 onChange
更新安装位置详细信息将保持原样。
注意:您应该注意状态更新要等到下一个渲染周期才会生效,因此在您的代码中您使用新值调用 setSize
但继续处理当前大小值的位置,因此您需要转发当前值。
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;
size.value
状态是_updateData
函数触发<select>
的onChange时的一个选项。例如,假设 <select>
的起始默认值选项为 "All",locations
为空...选项更改为 "Medium",locations
为 "All"...选项改为"Large",locations
为"Medium"...等。最初我希望 _updateData
函数在选择 "All" 的情况下 运行 加载,但它也不起作用,它抛出错误 Cannot read 属性 'target' of undefined on setSize({value: event.target.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"})
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;
首先useState函数是异步的。因此需要一些时间来更新。参见 Is useState synchronous?。 其次,当组件挂载时调用 useEffect 函数。所以还没有发生 onchange 事件。一旦您更改 select 标记中的选项,就会发生 onchange 事件。 只需从您的 useEffect 函数中删除 _updateData。
是的,您在安装 useEffect
挂钩中调用 select 的 onChange
处理程序,没有事件对象来取消引用 target
属性 .我会分解出 updateData
代码的其余部分,以便您可以使用初始状态值调用它。这将允许您使用初始大小状态日期 AND select 的 onChange
更新安装位置详细信息将保持原样。
注意:您应该注意状态更新要等到下一个渲染周期才会生效,因此在您的代码中您使用新值调用 setSize
但继续处理当前大小值的位置,因此您需要转发当前值。
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;