1

I'm using Django Rest Framework for API, and React JS for frontend, and I need to increment the count of downloads when the button is clicked. I've done that before in Django, but I have no idea how to do that with React. I tried to send the post request to API to increment the count of downloads, but I got this error: Failed to load resource: the server responded with a status of 405 (Method Not Allowed). How to fix it, or maybe there is another way to increment the count of downloads? Here is the data of API:

{
    "id": 3,
    "category": {
        "id": 11,
        "parent": {
            "id": 2,
            "name": "Name",
            "slug": "slug",
            "img": "Image",
            },
        "name": "system",
        "slug": null,
        "img": ""
    },
    "title": "Title",
    "content": "Content",
    "image": "",
    "pub_date": "2022-02-02",
    "counter": 0, // the count of downloads
    "file": "file",
    "in_archive": false
}

Do I need to create the state of the count of downloads, or there's another way to do that(maybe it has to be in Django)?

And here's how I'm trying to send the post request:

const SoftwareDetail = ({ match }) => {

  const [software, setSoftware] = useState([]);
  const [counter, setCounter] =  useState(null);
  const [file, setFile] = useState(null);

  const id = match.params.id;

  useEffect(() => {
    fetch(`http://127.0.0.1:8000/api/software/${id}/`)
        .then(response => response.json())
        .then(data => {
          setSoftware(data);
          setFile(data.file);
          setCounter(data.counter);
        })
  }, [id]);

  const onFetchData = () => {
    const requestOptions = {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
    };
    fetch(`http://127.0.0.1:8000/api/software/${id}/`, requestOptions)
        .then(response => response.json())
        .then(data => {
          data.counter = counter // I know this isn't right, but I actually don't know how it has to be
        })
  }

  const handleClick = () => {

    window.open(file);

    setCounter(counter => counter+1);

    useEffect(() => {
      onFetchData()
    }, [])
  }

  return (
    ...
    <Download handleClick={handleClick} />
    ...
  );
};

const Download = ({ handleClick }) => {
  return (
    ...
    <button id={styles["download-start"]} onClick={handleClick}>
      <strong>DOWNLOAD</strong>
      <small>FILE SIZE: 130 MB</small>
    </button>
    ...
  )
}
Albert
  • 95
  • 9
  • 2
    Your question says POST but your code is doing a PUT, which is it? And what is your Django handler expecting? And why is your API call not in a `useEffect` hook? – Jared Smith Mar 21 '21 at 10:40
  • I tried it with a PUT request, forgot to change. About `useEffect`, is it right(I edited the question)? – Albert Mar 21 '21 at 11:41

1 Answers1

1

Per your edited question:

No, if you try to run that you'll get an invalid hook call error, you can't use a hook in a callback. Typically to fetch in React with hooks you'll have a state variable like

const SomeFnComponent = () => {
  const [shouldFetch, setShouldFetch] = useState(false);
  useEffect(() => {
    if (shouldFetch) {
      fetch(...)
        .then(data => {
          dowhatever(data); 
          setShouldFetch(false);
        }); 
    }
  }, [shouldFetch]);

  const onClick = (evt) => setShouldFetch(true);
  return <button onClick={onClick}>Click me!</button>;
}

So on the initial render it hits the useEffect but nothing happens because the condition is false and it renders the button. Then later a user clicks the button, which triggers the state update to true, which causes a re-render And because the state variable is in the useEffect dependency array the effect runs, the condition is true, the fetch comes back, state gets update again (back to false), component re-renders, and now the condition is false so we're back to waiting on user input.

I realize this sounds complicated when I describe it in English, but as you can see from the template I banged out above it makes the code quite simple because React is doing most of the work for you behind the scenes. Most importantly you can easily see the data dependencies, especially if you're using a reducer on a more complicated state. If you had to wire all that up imperatively you'd have 10x the code and those manual DOM manipulations tend not to scale well in terms of maintainability.

Jared Smith
  • 19,721
  • 5
  • 45
  • 83
  • Thanks for the answer, of course I understand this part, but I needed to change the counter in the API inside `useEffect` on click – Albert Mar 21 '21 at 13:08
  • 1
    @Albert https://stackoverflow.com/questions/29775797/fetch-post-json-data – Jared Smith Mar 21 '21 at 13:47
  • gives an error: "Method "POST" not allowed" – Albert Mar 21 '21 at 14:20
  • Yeah, again, is your Django handler for that URL set up to handle POST requests? That's not a Javascript problem. I don't know Django but in every other web setup I've ever used (e.g. Flask, Express, PHP, Hapi) you have to specify in your routing which methods a given URL will accept. – Jared Smith Mar 21 '21 at 14:23
  • 1
    Then okay, it just seems to be okay in terms of Django. I thought the problem was in React, but it turns out not. Anyway, thanks for the answer, on the React part the issue has been resolved – Albert Mar 21 '21 at 14:29
  • @Albert you're welcome, I hope you get it working soon. If you haven't already, you might look at this: https://stackoverflow.com/questions/23124248/python-requests-post-request-data-with-django – Jared Smith Mar 21 '21 at 15:13