Issue
I am using useNavigate
to navigate from one page of my website to another when a button is clicked. When I run the site using npm start, when I click the button, the page reloads the current page and the url changes from 'http://localhost:3000' to 'http://localhost:3000/?'
The code is below:
const submitForm = async (e) => {
const res = await fetch("http://localhost:8000/properties")
const jsonData = await res.json()
setProperties(jsonData)
navigate("/list", { state: properties })
}
button element:
<button onClick={() => submitForm()}>Find Properties</button>
For configuring my routes in App.js, I simply have:
export default function App() {
return (
<BrowserRouter>
<div className="App">
<Routes>
<Route exact path="/" element={<FormComponent />} />
<Route path="/list" element={<ListComponent />} />
</Routes>
</div>
</BrowserRouter>
)
}
Solution
Issues
It seems this button is very likely rendered within a
form
element, and when the form is submitted the default form action is not being prevented and the page is reloading.Additionally, it appears the
submitForm
handler is enqueueing a React state update and then attempting to navigate with the expected updated state.const submitForm = async (e) => { const res = await fetch("http://localhost:8000/properties"); const jsonData = await res.json(); setProperties(jsonData); // <-- enqueued state update navigate("/list", { state: properties }); // <-- stale state }
In React, enqueued state updates are processed asynchronously. You can't enqueue a state update and expect to read the updated state value in the same function/callback scope the update was enqueued in.
button
elements havetype="submit"
by default if atype
attribute isn't provided.
Solution
- Move the
submitForm
callback handler to theform
element'sonSubmit
handler prop. - Call
preventDefault
on theonSubmit
event object. - Explicitly declare a button type.
- Pass the fetched JSON data directly in the
navigate
function, no need to update local state considering the component is likely unmounted soon.
Example:
const submitForm = async (e) => {
e.preventDefault(); // <-- prevent default form action
const res = await fetch("http://localhost:8000/properties");
const jsonData = await res.json();
navigate("/list", { state: jsonData }); // <-- pass current data
}
...
<form onSubmit={submitForm}>
...
<button type="submit">Find Properties</button>
...
</form>
Answered By - Drew Reese
Answer Checked By - - David Goodson (ReactFix Volunteer)