skip to Main Content

I am new to react and MongoDB, I am trying to add months to a date in my database in mongo, but it only updates the first time I click on the <Price> button, I need it to update every time I click it. The user has to log out and log back in for it to work again, but still only 1 update can be made to the database. Can someone explain to me why this is happening, and how could it be fixed?

This is the function

import React, { useContext } from "react";
import { useState } from "react";
import useFetch from "../../hooks/useFetch";
import Footer from "../../components/Footer";
import Navbar from "../../components/Navbar";
import Sidebar from "../../components/Sidebar";
import {
  ContractContainer,
  HeadingContainer,
  TypeH1,
  ActiveUntil,
  MonthlyWrapper,
  MonthlyContainer,
  MonthNumber,
  Price,
  Navbarback,
} from "./userinfoElements";
import { AuthContext } from "../../context/AuthContext";
import moment from "moment";
import axios from "axios";

const Userinfo = () => {
  // for nav bars
  const [isOpen, setIsOpen] = useState(false);

  // set state to true if false
  const toggle = () => {
    setIsOpen(!isOpen);
  };

  const { user } = useContext(AuthContext);

  let { data, loading, reFetch } = useFetch(`/contracts/${user.contractType}`);

  let dateFormat = moment(user.activeUntil).format("DD/MMMM/yyyy");

  const updateDate = async () => {
    try {
      let newDate = moment(user.activeUntil).add(1, "months");
      dateFormat = newDate.format("DD/MMMM/yyyy");
      axios.put(`/activedate/${user.namekey}`, {
        activeUntil: newDate,
      });
    } catch (err) {
      console.log(err);
    }
    reFetch();
  };

  return (
    <>
      <Sidebar isOpen={isOpen} toggle={toggle} />
      {/* navbar for smaller screens*/}
      <Navbar toggle={toggle} />
      <Navbarback /> {/* filling for transparent bacground navbar*/}
      {loading ? (
        "Loading components, please wait"
      ) : (
        <>
          <ContractContainer>
            <HeadingContainer>
              <TypeH1>{data.contractType}</TypeH1>
              <ActiveUntil>Subscription active until {dateFormat}</ActiveUntil>
            </HeadingContainer>
            <MonthlyWrapper>
              <MonthlyContainer>
                <MonthNumber>1 Month</MonthNumber>
                <Price onClick={updateDate}>{data.month1Price}$</Price>
              </MonthlyContainer>
              <MonthlyContainer>
                <MonthNumber>3 Month</MonthNumber>
                <Price onClick={updateDate}>{data.month3Price}$</Price>
              </MonthlyContainer>
              <MonthlyContainer>
                <MonthNumber>6Month</MonthNumber>
                <Price onClick={updateDate}>{data.month6Price}$</Price>
              </MonthlyContainer>
              <MonthlyContainer>
                <MonthNumber>12Month</MonthNumber>
                <Price onClick={updateDate}>{data.month12Price}$</Price>
              </MonthlyContainer>
            </MonthlyWrapper>
          </ContractContainer>
        </>
      )}
      <Footer />
    </>
  );
};

export default Userinfo;

this is the fetch hook

import { useEffect, useState } from "react";
import axios from "axios";

const useFetch = (url) => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const res = await axios.get(url);
        setData(res.data);
      } catch (err) {
        setError(err);
      }
      setLoading(false);
    };
    fetchData();
  }, [url]);

  const reFetch = async () => {
    setLoading(true);
    try {
      const res = await axios.get(url);
      setData(res.data);
    } catch (err) {
      setError(err);
    }
    setLoading(false);
  };

  return { data, loading, error, reFetch };
};

export default useFetch;

Any help is appreciated!

EDIT: added AuthContext file and server sided controllers if needed

import React from "react";
import { createContext, useEffect, useReducer } from "react";

const INITIAL_STATE = {
  user: JSON.parse(localStorage.getItem("user")) || null,
  loading: false,
  error: null,
};

export const AuthContext = createContext(INITIAL_STATE);

const AuthReducer = (state, action) => {
  switch (action.type) {
    case "LOGIN_START":
      return {
        user: null,
        loading: true,
        error: null,
      };
    case "LOGIN_SUCCESS":
      return {
        user: action.payload,
        loading: false,
        error: null,
      };
    case "LOGIN_FAILURE":
      return {
        user: null,
        loading: false,
        error: action.payload,
      };
    case "LOGOUT":
      return {
        user: null,
        loading: false,
        error: null,
      };
    default:
      return state;
  }
};

export const AuthContextProvider = ({ children }) => {
  const [state, dispatch] = useReducer(AuthReducer, INITIAL_STATE);

  useEffect(() => {
    localStorage.setItem("user", JSON.stringify(state.user));
  }, [state.user]);

  return (
    <AuthContext.Provider
      value={{
        user: state.user,
        loading: state.loading,
        error: state.error,
        dispatch,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};

api Controller to update active date

import User from "../models/User.js";

export const updateActiveDate = async (req, res, next) => {
  try {
    await User.updateOne({ $set: { activeUntil: req.body.activeUntil } });

    res.status(200).json("Active date has been updated.");
  } catch (err) {
    next(err);
  }
};

api Controller to find contracts

import Contracts from "../models/Contracts.js";

export const getContract = async (req, res, next) => {
  try {
    const Contract = await Contracts.findOne({
      contractType: req.params.contractType,
    });

    res.status(200).json(Contract);
  } catch (err) {
    next(err);
  }
};

api Controller for login authentication

export const login = async (req, res, next) => {
  try {
    const user = await User.findOne({ namekey: req.body.namekey });
    if (!user) return next(createError(404, "User not found!"));
    if (req.body.password === undefined) {
      return next(createError(500, "Wrong password or namekey!"));
    }
    const isPasswordCorrect = await bcrypt.compare(
      req.body.password,
      user.password
    );
    if (!isPasswordCorrect)
      return next(createError(400, "Wrong password or namekey!"));

    const token = jwt.sign({ id: user._id }, process.env.JWT);

    const { password, ...otherDetails } = user._doc;
    res
      .cookie("access_token", token, {
        httpOnly: true,
      })
      .status(200)
      .json({ details: { ...otherDetails } });
  } catch (err) {
    next(err);
  }
};

2

Answers


  1. Give this a try. It awaits the put request, and only once that has responded it calls reFetch. Without the await you’re calling the reFetch before the put request has had a chance to complete its work.

    const updateDate = async () => {
        try {
          let newDate = moment(user.activeUntil).add(1, "months");
          dateFormat = newDate.format("DD/MMMM/yyyy");
          await axios.put(`/activedate/${user.namekey}`, {
            activeUntil: newDate,
          });
        } catch (err) {
          console.log(err);
        } finally {
          reFetch();
        }
      };
    
    Login or Signup to reply.
  2. You should update the stored user state to reflect the activeUntil date change.
    Define a ‘UPDATE_USER_DATE’ action in your reducer to update the user instance:

    case "UPDATE_USER_DATE":
        const updatedUser = { ...state.user };
        updatedUser.activeUntil = action.payload;
        return {
            ...state,
            user: updatedUser
        };
    

    Then, after updating the date in updateDate, update the user state as well:

    const { user, dispatch } = useContext(AuthContext);
    
    const updateDate = async () => {
        try {
          let newDate = moment(user.activeUntil).add(1, "months");
          dateFormat = newDate.format("DD/MMMM/yyyy");
          await axios.put(`/activedate/${user.namekey}`, {
            activeUntil: newDate,
          });
          dispatch({ type: "UPDATE_USER_DATE", payload: newDate });
        } catch (err) {
          console.log(err);
        }
        reFetch();
      };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search