javascriptreact-native

How to make component mount only once?


I have two components Auth and Main, which represent the Login screen and the Main screen of the app. Both are called by component AuthOrApp, which chooses which one to display based on the user's state id.

//component AuthOrApp

import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';

import { login } from '../store/userSlice';
import { addShops } from '../store/shopsSlice';
import Auth from '../screens/Auth';
import Main from '../navigators/Main';
import { getData } from '../common';

export default props => {
    const [start, setStart] = useState(true);
    const dispatch = useDispatch();
    const user = useSelector(state => state.user);

    const fetchData = async () => {
        if (!user.id) {
            const userData = await getData();
            if (userData.id) {
                dispatch(addShops(userData.filterShops));
                dispatch(login({ name: userData.name, id: userData.id }));
            } else {
                setStart(false);
            }
        } else {
            setStart(false);
        }
    }

    useEffect(() => {
        fetchData();
    }, [user])

    return (!user.id) ? <Auth /> : <Main />;
}

When logging out, the user state id is cleared and the app returns to the login screen. The problem is that it mounts the Auth and Main component again.

I don't understand why it reassembles the components instead of just rendering. Could someone explain it to me? How do I stop it from mounting again?


Solution

  • 1. Render Both, Toggle Visibility

    function AuthOrApp() {
      const user = useSelector(state => state.user);
    
      return (
        <>
          <Auth style={{ display: !user.id ? 'block' : 'none' }} />
          <Main style={{ display: user.id ? 'block' : 'none' }} />
        </>
      );
    }
    

    1. Use React Router

    npm install react-router-dom
    
    
    
    // App.js or a central routing component
    import React from 'react';
    import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
    import { useSelector } from 'react-redux';
    
    import Auth from './screens/Auth';
    import Main from './navigators/Main';
    
    const App = () => {
      const user = useSelector(state => state.user);
    
      return (
        <Router>
          <Routes>
            <Route 
              path="/login" 
              element={!user.id ? <Auth /> : <Navigate to="/main" />} 
            />
            <Route 
              path="/main" 
              element={user.id ? <Main /> : <Navigate to="/login" />} 
            />
            {/* Redirect any unknown routes */}
            <Route 
              path="*" 
              element={<Navigate to={user.id ? "/main" : "/login"} />} 
            />
          </Routes>
        </Router>
      );
    }
    
    export default App;
    
    
    
    // In your logout function/component
    import { useDispatch } from 'react-redux';
    import { logout } from '../store/userSlice';
    import { useNavigate } from 'react-router-dom';
    
    const LogoutButton = () => {
      const dispatch = useDispatch();
      const navigate = useNavigate();
    
      const handleLogout = () => {
        dispatch(logout()); // Clears user.id
        navigate('/login'); // Redirects to login
      };
    
      return <button onClick={handleLogout}>Logout</button>;
    };
    

    If you want to preserve the state of a component even when it's unmounted (e.g., user partially filled a form on the Auth screen and doesn't want to lose that information upon navigating away), you can manage the state globally using Redux or React's Context API.