Apply auth changes that affect to session and react parent component

Viewed 153

I'm going to do my best to explain my problem here, but it's going to be challenging so bear with me.

I have a web app developed in react + firebase with firebase authentication, the login is for now only handled with the mail and password provider. I'm using react router and firebase 9.

When someone logs in or out the auth token is persisted in session storage, and I have a condition in the App component (the first layer on my page) that shows a log in/register link if the user isn't logged in and a my account link if the user is logged in. The problem is that I need to reload the page to see the changes, When logging in you get redirected to another route, but being a react app it doesn't reload the whole page.

My App.js

  const auth = getAuth();
  const App = () => {
  const [user, setUser] = useState();
  const [expanded, setExpanded] = React.useState(true);
  const [activeKey, setActiveKey] = React.useState('1');

  const handleLogout = () => {
    signOut(auth).then(()=> {
      setUser({});
      sessionStorage.clear();
      console.log("unset user");
    }).catch((err) => {
      console.error(err);
      alert(err.message);
    })
  };

  onAuthStateChanged(auth, (user) => {
    if (user) {
      setUser(user)
      sessionStorage.setItem("user", JSON.stringify(user))
      const uid = user.uid;
      console.log("set user")

    } else {

    }
  });

    useEffect(() => {
      const loggedInUser = sessionStorage.getItem("user");
      if (loggedInUser != null) {
        const foundUser = loggedInUser.toString();
        setUser(foundUser);
        console.log(user)
      }
    }, []);
    return (
      <div>

              <li className="nav-item">
                {
                  (sessionStorage.user ?
                    <div className="d-flex justify-content-around row">
                      <Link className="nav-link" to="/user/perfil">Mi cuenta</Link>
                    </div>
                    : <Link className="nav-link" to="/login">Iniciar sesión / Registrarse</Link>
                  )
                }

          </li>
  </div>
)

}

export default App;

My login.js

const auth = getAuth();
console.log(auth.currentUser);
const loginWithEmailAndPassword = async (email, password) => {

setPersistence(auth, browserSessionPersistence)
    .then(() => {
        signInWithEmailAndPassword(auth, email, password)

    }).catch((error) => {
        const errorCode = error.code;
        const errorMessage = error.message;
        alert(error.message)
    })
}

class Login extends Component {
constructor() {
    super();
    this.state = {
        email: "",
        password: "",
        redirect: "",
    };
}
handleChangeEmail = () => {
    this.setState({ email: document.getElementById("email").value });
    console.log(this.state);
}
handleChangePassword = () => {
    this.setState({ password: document.getElementById("password").value })
}
handleRedirect = () => {
    this.setState({ redirect: "/aplicacion" });
    console.log(this.state);
}
handleClick = async () => {
    try {
        await loginWithEmailAndPassword(
            this.state.email,
            this.state.password
        );
        this.handleRedirect();
    } catch (err) {
        console.error(err);
        alert(err.message);
    }
};
render() {

    if (this.state.redirect) {
        return <Navigate to={this.state.redirect} />
    }
    return (
        <div className="d-flex justify-content-center" >
                                    <div className="col-6">
                                        <Link to='/recuperacion' className="tiny-text">He olvidado mi contraseña</Link>
                                    </div>
        </div>
    )
}

}

EDIT: I'm currently working on a redirect detection function for another part of the aplication that is going to be sitting in the App.js , currently it's just an array that checks if the last two routes are equal to the redirect cases, it's almost too bruteforce for my taste, but it's what i've got for now.

1 Answers

Ok, I ended up solving the redirection issue by using states and hooks let me explain:

I created a new state called redirectState and its setter and a navigate one:

 const [redirectState, setRedirectState] = useState()
 let navigate = useNavigate()

Then I just have to set it when the auth state changes so the app knows when to redirect and when not to:

onAuthStateChanged(auth, (user) => {
if (user) {
  setUser(user)
  sessionStorage.setItem("user", JSON.stringify(user))
  const uid = user.uid;
  console.log("set user");
  console.log(uid)
  setRedirectState("redirect")
} else {
  sessionStorage.clear();
  setRedirectState()
}});

Finally I used the react router useNavigate hook to have direct control over redirections:

  useEffect(() => {
const loggedInUser = sessionStorage.getItem("user");
console.log("checking user...")
if (loggedInUser != undefined) {
  const foundUser = loggedInUser.toString();
  setUser(foundUser);
  console.log(user);
  navigate("/aplicacion")
}else{
  console.log("no user")
  navigate("/")
}}, [redirectState]);

It may seem crude but the functionality works as of right now, when the navigate functions are called all components update, that way the app shows the correct link every time, there are some misplace resources which I want to fix, but that will be at another point, will keep this post updated if i find ways to improve the way it's coded.

But for now I'd consider it solved! :-)

Related