*Using react-router-dom and react.js
I have two different set of radio buttons. One set has 2 buttons while the other set has 3. I want to navigate to a new page whenever a user clicks on two buttons (one in each set). We have a total of six different outcomes, therefore 6 different pages to navigate to. It seems to work fine but with one problem: it only works when we click on a button for a second time. Example: Clicking on "Messages" & "Global" doesn't work initially and doesn't do anything but then if we click on a different button, then it navigates to the initial set of buttons we clicked.
Does anyone know how to fix this issue? Thank you.
import { useNavigate } from 'react-router-dom';
export default function DisplayHomeOptions() {
let navigate = useNavigate();
const [formData, setFormData] = React.useState({ location: "", selector: "" })
function handleChange(event) {
const { name, value, type, checked } = event.target
setFormData(prevFormData => {
return {
...prevFormData,
[name]: type === "checkbox" ? checked : value
}
})
}
function test() {
return (
formData.location === "National" && formData.selector === "Polls" ? navigate("/np") :
formData.location === "Global" && formData.selector === "Questions" ? navigate("/gq") :
formData.location === "Global" && formData.selector === "Polls" ? navigate("/gp") :
formData.location === "National" && formData.selector === "Messages" ? navigate("/nm") :
formData.location === "National" && formData.selector === "Questions" ? navigate("/nq") :
formData.location === "Global" && formData.selector === "Messages" ? navigate("/gm") : null
)
}
return (
<div>
<fieldset>
<legend>Option #1</legend>
<input
type="radio"
name="location"
value="Global"
onChange={handleChange}
onClick={test}
/>
<label htmlFor="Global">Global</label>
<input
type="radio"
name="location"
value="National"
onChange={handleChange}
onClick={test}
/>
<label htmlFor="National">National</label>
</fieldset>
<fieldset>
<legend>Option #2</legend>
<input
type="radio"
name="selector"
value="Messages"
onChange={handleChange}
onClick={test}
/>
<label htmlFor="Messages">Messages</label>
<input
type="radio"
name="selector"
value="Questions"
onChange={handleChange}
onClick={test}
/>
<label htmlFor="Questions">Questions</label>
<input
type="radio"
name="selector"
value="Polls"
onChange={handleChange}
onClick={test}
/>
<label htmlFor="Polls">Polls</label>
</fieldset>
</div>
)
}```
CodePudding user response:
Issue
The issue is that React state updates aren't immediately processed, they are enqueued and asynchronously processed later. The formData
state update from handleChange
is not yet available when test
function is called as the same time.
Solution
It seems you want the act of navigation to be an effect of selecting from the radio buttons. Move the test
function logic into an useEffect
hook to issue the imperative navigation.
Example:
export default function DisplayHomeOptions() {
const navigate = useNavigate();
const [formData, setFormData] = React.useState({
location: "",
selector: ""
});
useEffect(() => {
const { location, selector } = formData;
switch (true) {
case location === "National" && selector === "Polls":
navigate("/np");
break;
case location === "Global" && selector === "Questions":
navigate("/gq");
break;
case location === "Global" && selector === "Polls":
navigate("/gp");
break;
case location === "National" && selector === "Messages":
navigate("/nm");
break;
case location === "National" && selector === "Questions":
navigate("/nq");
break;
case location === "Global" && selector === "Messages":
navigate("/gm");
break;
default:
// ignore
}
}, [formData]);
function handleChange(event) {
const { name, value, type, checked } = event.target;
setFormData((prevFormData) => {
return {
...prevFormData,
[name]: type === "checkbox" ? checked : value
};
});
}
return (
<div>
<fieldset>
<legend>Option #1</legend>
<input
type="radio"
name="location"
value="Global"
onChange={handleChange}
/>
<label htmlFor="Global">Global</label>
<input
type="radio"
name="location"
value="National"
onChange={handleChange}
/>
<label htmlFor="National">National</label>
</fieldset>
<fieldset>
<legend>Option #2</legend>
<input
type="radio"
name="selector"
value="Messages"
onChange={handleChange}
/>
<label htmlFor="Messages">Messages</label>
<input
type="radio"
name="selector"
value="Questions"
onChange={handleChange}
/>
<label htmlFor="Questions">Questions</label>
<input
type="radio"
name="selector"
value="Polls"
onChange={handleChange}
/>
<label htmlFor="Polls">Polls</label>
</fieldset>
</div>
);
}
An optimization may be to declare a Map of target paths from the location
and selector
values as keys.
const navTarget = {
National: {
Messages: "/nm",
Polls: "/np",
Questions: "/nq",
},
Global: {
Messages: "/gm",
Polls: "/gp",
Questions: "/gq",
}
}
...
useEffect(() => {
const { location, selector } = formData;
if (location && selector) {
const target = navTarget[location][selector];
if (target) {
navigate(target);
}
}
}, [formData]);
CodePudding user response:
onClick
event fires before onChange
event. To account for this, you could move navigation into the onChange
event handler.