Home > Software design >  React state inside React-context is not updating
React state inside React-context is not updating

Time:10-03

Hello guys so I have the following problem:

  • I have a login form after user successfully provide the right info for signing in, I store the user object and access token in the AuthContext

  • I protect the home route using the context

  • Problem => the react state inside the context is not being updated

-[Edit] Solution => I found the solution and it was only changing the following code: const {setAuth} = useAuth(); to the code: const {setAuth} = useAuth({});

-[Edit 2] => Because I am a beginner I also discovered that navigation between components with anchor tag () or window location cause the lose of state data so I should use Link from react router dom to avoid re-rendering

AuthProvider.js

import { createContext, useState } from "react";

const AuthContext = createContext({});

export const AuthProvider = ({ children }) => {
    const [auth, setAuth] = useState({});

    return (
        <AuthContext.Provider value={{ auth, setAuth }}>
            {children}
        </AuthContext.Provider>
    )
}

export default AuthContext;

App.js

function App() {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        {/* public routes */}
        <Route path="auth" element={<AuthLayout />}>
          <Route path="login" element={<Login />} />
          <Route path="register" element={<Register />} />
          <Route path="unauthorized" element={<Unauthorized />} />
        </Route>
        {/* we want to protect the following routes */}
        {/* RequireAuth having outlet => return child only if context auth has user object */}
        <Route element={<RequireAuth />}>
          <Route path="home" element={<Home />} />
        </Route>
        {/* catch all */}
      </Route>
    </Routes>
  );
}

export default App;

RequireAuth.js [ Problem is here, the auth is always empty]

const RequireAuth = () => {
  const { auth } = useAuth();
  const location = useLocation();
  return auth?.userObject ? (
    // we used from location and replace so we want to save the previous location of the visited page by user so he is able to go back to it
    <Outlet />
  ) : (
    <Navigate to="/auth/login" state={{ from: location }} replace />
  );
};

export default RequireAuth;

Login.js [Here I update the state]

const handleFormSubmission = async (data,e) => {
    e.preventDefault();
    try {
      const response = await axios.post(
        ApiConstants.LOGIN_ENDPOINT,
        JSON.stringify({
          Email: email,
          Password: password,
        }),
        {
          headers: ApiConstants.CONTENT_TYPE_POST_REQUEST,
        }
      );

      //const roles = ;
      const userObject = response?.data;
      const accessToken = response?.data?.token;
      setAuth({ userObject, password, accessToken });
      console.log(userObject);
      console.log(accessToken);
      console.log(password);

      message.success("You are successfully logged in");

index.js

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { AuthProvider } from "./context/AuthProvider";

import "./styles/ant-design/antd.css";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <AuthProvider>
        <Routes>
          <Route path="/*" element={<App />} />
        </Routes>
      </AuthProvider>
    </BrowserRouter>
  </React.StrictMode>
);

CodePudding user response:

Did you import setAuth correctly? Did you call setAuth({}) inside handleFormSubmission function?

CodePudding user response:

I have gone through the code and there are few suggestions which will make code work.

import { createContext, useState } from "react";

const AuthContext = createContext({});

export const AuthProvider = ({ children }) => {


const [auth, setAuth] = useState({});
  function updateAuth(authVal) {
    console.log("update auth called with value", authVal);
    setAuth(authVal);
  }

return (
<AuthContext.Provider value={{ auth, updateAuth }}>
  {children}
</AuthContext.Provider>
);
};

export default AuthContext;

Also we need few changes in the consumer code which looks like below

const { updateAuth } = useContext(AuthContext);
const handleFormSubmission = async (e) => {
e.preventDefault();
try {
  const response = await axios.get("https://catfact.ninja/breeds? 
    limit=1");

  //const roles = ;
  const userObject = response?.data;
  console.log("cats api data response is ", userObject);
  updateAuth(userObject);
  //setAuth({ userObject, password, accessToken });
} catch (err) {
  console.error("some error");
}
};

I have made sample application on codesandbox for the same as well. Please find url as https://codesandbox.io/s/funny-scott-n7tmxs?file=/src/App.js:203-689

I hope this help and have a wonderful day :)

  • Related