Home > Net >  React Router Link for button that makes an HTTP request
React Router Link for button that makes an HTTP request

Time:09-17

I'm trying to use Link, from React Router, to make a button take the user to another page in the app.

However, the button should trigger an HTTP POST request through a handler function and only then take the user to this other page, provided there are no errors.

This is my Link JSX element:

<Link to="/next-page">
   <Button onClick={e => { handleSubmit(e) }}>Go</Button>
</Link>

And this is the handler function:

const handleSubmit = (e) => {
        e.preventDefault();

        axios.post('http://localhost:8000/page', pageData)
            .then(res => console.log(res.data))
            .catch(err => console.log(err));
}

The current behavior is that the HTTP request works fine, but the button doesn't take the user to the desired page (even though the link address is apparently present, which I confirmed by inspecting the button in the browser).

Does the fact that axios.post() returns a promise breaks the expected behavior?

How should I deal with it?

CodePudding user response:

As it is written here, preventDefault() method stops the default action of a selected element from happening by a user, that may be the reason why the button doesn't take the user to the desired page.

The preventDefault() method of the Event interface tells the user agent that if the event does not get explicitly handled, its default action should not be taken as it normally would be.

To achieve the action you want you can use useHistory hook (read more here)

You can remove Link from JSX code, and use the button only:

<Button onClick={e => { handleSubmit(e) }}>Go</Button>

Then you'll need to update the event handler in order to redirect the user after request happen:

const handleSubmit = (e) => {
    axios.post('http://localhost:8000/page', pageData)
        .then(res => {
            console.log(res.data)
    
            history.push("/next-page");
        })
        .catch(err => console.log(err));
}

CodePudding user response:

It appears you may be preventing some default form action from occurring, but not default link action. The button's onClick event bubbles up and the link is activated.

It's generally considered anti-pattern to nest an interactive component within another interactive component, but if you want the visual of a button with the behavior and semantics of an HTML link, you can implement the following:

The onClick handler should be moved to the Link component and the default action prevented on the click event so the navigation link action isn't immediately called, i.e. don't immediately navigate. Run the asynchronous logic and only when the POST request succeeds issue an imperative navigation. Add a basic onClick handler to the button the continues to prevent its default action.

Example:

const navigate = useNavigate();

...

const handleSubmit = async (e, target) => {
  e.preventDefault();

  try {
    const res = await axios.post('http://localhost:8000/page', pageData)
    console.log(res.data);
    navigate(target);
  } catch(err) {
    console.log(err);
  }
};

...

<Link to="/next-page" onClick={e => handleSubmit(e, "/next-page")}>
  <Button onClick={e => e.preventDefault()}>Go</Button>
</Link>

Or alternatively the button's onClick handler can also stop the propagation of the click event so the link is never activated.

const navigate = useNavigate();

...

const handleSubmit = async (e, target) => {
  e.preventDefault();
  e.stopPropagation();

  try {
    const res = await axios.post('http://localhost:8000/page', pageData)
    console.log(res.data);
    navigate(target);
  } catch(err) {
    console.log(err);
  }
};

...

<Link to="/next-page">
  <Button onClick={e => handleSubmit(e, "/next-page")}>
    Go
  </Button>
</Link>
  • Related