skip to Main Content

I have a POST endpoint that creates an instance on the database once the user creates an account, the rows are filled with information like day of the week, date, user_id, etc.

@api_view(['POST'])
def generate_focus_instance_v2(request):
    try:
        user_id_from_request = request.data['user']
        if UserProgressReport.objects.filter(user=user_id_from_request):
            return Response({"message": "user instance already exists"})

        instance_serializer = UserProgressReportSerializer(data=request.data)
        if instance_serializer.is_valid():
            instance_serializer.save()
            return Response({"message": "user instance created", "data": instance_serializer.data})
        else:
            return Response({"message": "something went wrong", "data": instance_serializer.errors})
    except Exception as error:
        return Response({"message": str(error)})

Here is the React section of my code

  async function handleFocusInstanceCreation() {
    try {
      const response = await fetch(
        `http://127.0.0.1:8000/api/v1/create/focus_instance`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            user: currentUser.user_id,
            focus_time: 0.0,
            day_of_the_week: currentDayOfTheWeek,
            date: currentDate,
            week_number: currentWeekNumber,
          }),
        }
      );

      const data = await response.json();
      console.log(data);
    } catch (error) {
      throw new Error(error);
    }
  }


useEffect(() => {
    if (currentUser && currentUser.user_id) {
      handleFocusInstanceCreation();
      console.log("Focus Instance creation called");
    } else {
      console.log("not logged");
    }
  });

The code logic is working as I intended, but because of the double rendering problem of react, my component is being called two times, and that is leading to creating two instances on the database for the same user.

In other words, once the user is redirected to the Home component after creating an account I see two response messages from the API call.

After some research and testing, I know that if I remove the <React.StrictMode> tag my problem goes away, but I don’t know the long-term implications of doing that. (Im kind new to react)

So my question is: should I remove the <React.StrictMode> tag or should I add a feature that limits to one the number of times that my endpoint will be called?

2

Answers


  1. You’re using useEffect without any dependencies, which means the side-effect code there will be called every time the component re-renders (and indeed, twice (or more – React is at liberty of doing that!) if using strict mode).

    You very probably don’t want to use useEffect for data submission anyway; just do it when the user has entered the data and hits a submit button or whatnot.

    You could (and maybe should) also make your API and/or data model resilient against this: e.g. add unique-together constraints for user-date pairs, or whatever makes sense; in real life your users will double-click that save button.

    Login or Signup to reply.
  2. To give my share of knowledge, StrictMode will renders components twice (only dev mode but not in production mode) to detect any problems within the script and let you know about them. It is a helpful tool for development, and removing it won’t address the root cause of the double rendering issue.

    For example, your app is wrapped by <React.StrictMode>:

      ReactDOM.render(
         <React.StrictMode>
           {app}
         </React.StrictMode>,
        document.getElementById('root')
      );
    

    If so, you can still disable StrictMode by removing the tag from the script:

      ReactDOM.render(
        {app} 
        document.getElementById('root')
      );
    

    Solution

    • Removing the StrictMode will be the last solution without finding the root cause.
    • Limiting the number of times the endpoint should be valuable feature when considered security and scaling like based on use cases.

    Adding an endpoint check for validation with meaningful response to ensure no process is done when a duplicate request is received

    For example

    @api_view(['POST'])
    def generate_focus_instance_v2(request):
        try:
            user_id_from_request = request.data.get('user')
            existing_instance = UserProgressReport.objects.filter(user=user_id_from_request).first()
            if existing_instance:
                return Response({"message": "user instance already exists", "data": UserProgressReportSerializer(existing_instance).data})
    
            instance_serializer = UserProgressReportSerializer(data=request.data)
            if instance_serializer.is_valid():
                instance_serializer.save()
                return Response({"message": "user instance created", "data": instance_serializer.data})
            else:
                return Response({"message": "something went wrong", "data": instance_serializer.errors})
        except Exception as error:
            return Response({"message": str(error)})
    

    Or Database constraint can be the solution to ensure only one user is created for each user.

    lets say, you can add a unique constraint on the user field in your UserProgressReport model
    Something like this

    class UserProgressReport(models.Model):
        user = models.ForeignKey(User, on_delete=models.CASCADE)
        
        class Meta:
            unique_together = ('user',)
    

    Additionally, when using useEffect, to avoid ,ultiple api calls, you should provide an empty dependency array to ensure that the effect only runs once, immediately after the initial render

    useEffect(() => {
      if (currentUser && currentUser.user_id) {
        handleFocusInstanceCreation();
        console.log("Focus Instance creation called");
      } else {
        console.log("not logged");
      }
    }, []);
    

    If you have specific dependencies that you want to trigger the effect, you should include those dependencies in the array.

    For example, if you want the effect to run whenever currentUser changes, you can include it in the dependency array

    useEffect(() => {
      if (currentUser && currentUser.user_id) {
        handleFocusInstanceCreation();
        console.log("Focus Instance creation called");
      } else {
        console.log("not logged");
      }
    }, [currentUser]);
    

    Final thought – Using an empty dependency array ([]) is suitable if you want the effect to run only once after the initial render.

    Hope this helps!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search