Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

React Context Re-rendering Issue when Using Context for User Login

I am encountering an issue with React context while implementing user login functionality on my website. Users can log in as dummy users, and I’ve implemented this using React contexts. However, after a user logs in and navigates away from the page, the context doesn’t persist, leading me to believe that the component is re-rendering.

Gif of issue

GitHub: GitHub Repository

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Additionally, you can experience the issue on the live website: Live Website

I would appreciate any assistance in understanding why the context is not persisting after navigating away from the page. Thank you for your help.

import { createContext, useState } from "react";

export const UserContext = createContext();

export const UserProvider = (props) => {
  const [user, setUser] = useState("grumpy19");
  return (
    <UserContext.Provider value={{ user, setUser }}>
      {props.children}
    </UserContext.Provider>
  );
};
import { useEffect, useState } from "react";
import { getUsers } from "../../utils/utils";
import { UserContext } from "../../contexts/UserContent";
import { useContext } from "react";
import "./Users.css";
import Spinner from "react-bootstrap/Spinner";
import { Card, CardGroup } from "react-bootstrap";
import { Badge } from "react-bootstrap";
import Stack from "react-bootstrap/Stack";

export default function Users() {
  const [users, setUsers] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [err, setErr] = useState(null);
  const { setUser, user } = useContext(UserContext);

  function handleAvatarClick(username) {
    setUser(username);
  }

  useEffect(() => {
    setIsLoading(true);
    setErr(false);
    getUsers()
      .then(({ users }) => {
        setUsers(users);
        setIsLoading(false);
      })
      .catch((err) => {
        setIsLoading(false);
        setErr(err);
      });
  }, []);

  if (err)
    return (
      <div>
        <h1>Whoops</h1>
        <h2>Status: {err.status}</h2>
        <h2>{err.msg}</h2>
      </div>
    );

  return (
    <div className="user-container">
      <h2 id="user-heading">Users</h2>
      <p id="user-desc">Click on a users image to log in</p>
      <p id="current-user">
        Hello <Badge bg="dark">{user}</Badge> !
      </p>

      {isLoading ? (
        <Spinner animation="border" variant="primary" className="spinner" />
      ) : null}

      <CardGroup id="users-list">
        <ul>
          {users.map((person, index) => {
            return (
              <Card
                key={index}
                className="user-card"
                onClick={() => {
                  handleAvatarClick(person.username);
                }}
              >
                <Stack className="user-info" direction="horizontal" gap={3}>
                  <div className="p-2">{person.name}</div>
                  <div className="p-2 ms-auto">
                    {" "}
                    <Badge>{person.username}</Badge>
                  </div>
                </Stack>

                <Card.Img src={person.avatar_url} />
              </Card>
            );
          })}
        </ul>
      </CardGroup>
    </div>
  );
}

I have implemented similar code in a previous project, and it worked without any problems.

import { createContext, useState } from "react";

export const UserContext = createContext();

export const UserProvider = (props) => {
  const [user, setUser] = useState("Paul-R");
  return (
    <UserContext.Provider value={{ user, setUser }}>
      {props.children}
    </UserContext.Provider>
  );
};
import { useEffect, useState } from "react";
import "./Users.css";
import { getUsers } from "../../utils.js/api";
import { UserContext } from "../../contexts/UserContent";
import { useContext } from "react";

export default function Users() {
  const [users, setUsers] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [err, setErr] = useState(null);
  const { setUser } = useContext(UserContext);

  function handleAvatarClick(username) {
    setUser(username);
  }

  useEffect(() => {
    setIsLoading(true);
    setErr(false);
    getUsers()
      .then(({ users }) => {
        setUsers(users);
        setIsLoading(false);
      })
      .catch((err) => {
        setIsLoading(false);
        setErr(err);
      });
  }, []);

  if (isLoading) return <p>Loading...</p>;

  if (err)
    return (
      <div>
        <h1>Whoops</h1>
        <h2>Status: {err.status}</h2>
        <h2>{err.msg}</h2>
      </div>
    );

  return (
    <div>
      <h1>Users</h1>
      <p>Click on a users image to log in</p>
      <ul>
        {users.map((user) => {
          return (
            <li key={user.username}>
              <p>username: {user.username}</p>
              <p>kudos: {user.kudos}</p>
              <img
                src={user.avatar_url}
                alt=""
                width="100"
                onClick={() => {
                  handleAvatarClick(user.username);
                }}
              />
            </li>
          );
        })}
      </ul>
    </div>
  );
}

>Solution :

The problem is that react-bootstrap’s <Nav.Link> component does not know you’re using react-router and so doesn’t integrate with it. When you click the link, it is doing a page refresh, which resets all states in the app.

You can use react-router’s Link component as a standalone, like this:

import { Link } from 'react-router';
// ...
<Link to="/">Home</Link>

Or if you want to continue using react-bootstrap’s Nav.Link, the as prop can be used to tell it to render using a different component:

<Nav.Link as={Link} to="/">Home</Nav.Link>

Or, one more option, you could write an onClick handler and have it call the function from useNavigate:

export default function Navigation() {
  const navigate = useNavigate)9;
  // ...
  <Nav.Link onClick={() => navigate("/")}>Home</Nav.Link>
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading