Home > OS >  Database updates only on the first click of the button with this functions, what is wrong and how co
Database updates only on the first click of the button with this functions, what is wrong and how co

Time:08-25

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);
  }
};

CodePudding user response:

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();
  };

CodePudding user response:

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();
    }
  };
  • Related