0

I am new to react and MongoDB, I have this site where the user can log in and look at his detail like name…, coming from the db. I am trying to find a way to update what is shown to the client when a change is made directly from the database, or when a button is clicked refresh user data, so it shows newly updated data from the database. I am using useReducer for authentication of the user and make updates to him from the client, but I can't find a way to update the data shown to the client. If changes are made directly from the db, the user has to log out and log back in to see the updated version of them. Again, I am very new to useReducer and have only managed to barely make it work from what I could find, but I can't find this particular problem I have anywhere. If someone could help, I greatly appreciate it. Tell me if you need more context or any other file.

My AuthContext file with useReducer

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,
      };
    case "UPDATE_USER_DATE":
      const updatedUser = { ...state.user };
      updatedUser.activeUntil = action.payload;
      return {
        ...state,
        user: updatedUser,
      };
    case "UPDATE_USER":
      const updateUser = { ...state.user };
      return {
        ...state,
        user: updateUser,
      };
    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>
  );
};

where i am trying to make the update happen

import React, { useContext } from "react";
import { useState } from "react";
import useFetch from "../../hooks/useFetch";
import Footer from "../../components/OutFooter";
import Navbar from "../../components/OutNavbar";
import Sidebar from "../../components/OutSidebar";
import {
  ContractContainer,
  HeadingContainer,
  TypeH1,
  ActiveUntil,
  MonthlyWrapper,
  MonthlyContainer,
  MonthNumber,
  Price,
  Navbarback,
} from "./userinfoElements";
import { AuthContext } from "../../context/AuthContext";
import { Navigate } from "react-router-dom";
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, dispatch } = useContext(AuthContext);
  if (!user) {
    return <Navigate to="/" />;
  }

  const { data } = useFetch(`/contracts/${user.contractType}`);

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

  const update1Month = async () => {
    try {
      let newDate = moment(user.activeUntil).add(30, "days");
      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);
    }
  };



  const update3Month = async () => {
    try {
      let newDate = moment(user.activeUntil).add(90, "days");
      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);
    }
  };
  const update6Month = async () => {
    try {
      let newDate = moment(user.activeUntil).add(180, "days");
      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);
    }
  };
  const update12Month = async () => {
    try {
      let newDate = moment(user.activeUntil).add(365, "days");
      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);
    }
  };
  const refreshUser = async () => {
    try {
      dispatch({ type: "UPDATE_USER" });
    } catch (err) {
      console.log(err);
    }
  };
  return (
    <>
      <Sidebar isOpen={isOpen} toggle={toggle} />
      {/* navbar for smaller screens*/}
      <Navbar toggle={toggle} />
      <Navbarback /> {/* filling for transparent bacground navbar*/}
      <>
        <ContractContainer>
          <button onClick={refreshUser}>toooo</button>
          <TypeH1>
            Hello {user.fName} {user.lName}!
          </TypeH1>
          <HeadingContainer>
            <TypeH1>{data.contractType}</TypeH1>
            <ActiveUntil>Subscription active until {dateFormat}</ActiveUntil>
          </HeadingContainer>
          <MonthlyWrapper>
            <MonthlyContainer>
              <MonthNumber>1 Month</MonthNumber>
              <Price onClick={update1Month}>{data.month1Price}$</Price>
            </MonthlyContainer>
            <MonthlyContainer>
              <MonthNumber>3 Month</MonthNumber>
              <Price onClick={update3Month}>{data.month3Price}$</Price>
            </MonthlyContainer>
            <MonthlyContainer>
              <MonthNumber>6Month</MonthNumber>
              <Price onClick={update6Month}>{data.month6Price}$</Price>
            </MonthlyContainer>
            <MonthlyContainer>
              <MonthNumber>12Month</MonthNumber>
              <Price onClick={update12Month}>{data.month12Price}$</Price>
            </MonthlyContainer>
          </MonthlyWrapper>
        </ContractContainer>
      </>
      <Footer />
    </>
  );
};

export default Userinfo;

in particular ->

 const refreshUser = async () => {
    try {
      dispatch({ type: "UPDATE_USER" });
    } catch (err) {
      console.log(err);
    }
  }; 

Mega Animenia
  • 15
  • 1
  • 6

2 Answers2

0

I doubt you can cause a re-render by changing a document directly from the database. However, if you want a refresh button, you can first create a new state

const [refresh, setRefresh] = React.useState(false)

then pass this onClick handler to a refresh button which is in the same component as your user details stuff like

<button onClick = {() => setRefresh(prev => !prev)}>Refresh</button>
  • thank you for the response, as i said i am very new to this and this got me confused, if you happened to have a link for an explenation oh how this stuff works i would appreciate it but i don't get it where should i put the first part `const [refresh, setRefresh] = React.useState(false) ` i see that `refresh` is established but not used, how would that work? and what dies prev stand for here, sorry for the bother again – Mega Animenia Aug 29 '22 at 18:20
  • You can put it anywhere as long as its inside the body of the function. Thats actually a shorthand for a callback function inside `setRefresh()`you can alternatively write it as `setRefresh((prevState) => { const newState = !prevState; return newState }` The prevState is implicitly passed in the callback function just like an `event` is passed in event handlers. As you may have noticed, `prev` is just the current value `refresh` is holding. Changing `refresh` will cause a re-render. So each time `setRefresh` is called, `refresh` changes from true to false or vice versa. – Arjun Tanwar Aug 30 '22 at 00:29
  • put the state and button in the component that you wish to re-render – Arjun Tanwar Aug 30 '22 at 00:32
  • toggle would be a better name than refresh to be honest – Arjun Tanwar Aug 30 '22 at 02:12
  • would `setRefresh(prev => !prev)` be the same as `setRefresh(!refresh)` ? – Mega Animenia Aug 30 '22 at 10:20
  • either way this isnt working for me, idk if you got my question right, am trying to make it so when i update something directly from the database example i have {user.fName} written above for users name, if i change that name directly in the database, and refresh my page or click a button to refresh, i want it to show the new name for the user, currently this happens only if the user logs out and logs back in. is there a way i could refetch the data from the db altogether which i am using above without loging out and in agaom? – Mega Animenia Aug 30 '22 at 10:30
0

After dabbling here and there, I resulted accidentally exactly what I needed. I found that using useEffect I can load a function at the start of the page loading. Accidentally doing this also resulted in updating my local stored user to the one in my database. I changed some things in my context file and added a new controller on api side that posts already stored users. If anyone requires further explanations or examples feel free to ask!

Mega Animenia
  • 15
  • 1
  • 6