0

I want to redirect users to Unprotected (login/ register) components if the user is not logged in (or verified) else allow access to Protected components.

To achieve that, I tried to use,


ProtectedRoute techniques

  • PrivateRoute - failed to implement appropriately
  • RequiresLogin - This helped me to reach to the next approach
  • And some YouTube videos and articles found from Google

Code:

index.js

...
<Router>
    <App />
</Router>
...

App.js - using the ProtectedRoute

...
<Routes>
    <ProtectedRoute exact path={"page1"} element={<Page1 />}/>
    <ProtectedRoute exact path={"page2"} element={<Page2 />}/>
    <Route exact path={"login"} element={<Login />}/>
<Routes/>
...

RequireAuth

It seemed that it is a better approach then ProtectedRoute,

  • RequireAuth - works except, it is ProtectiveOverflow at the moment

Code:

index.js

// Unchanged

App.js

...
<Routes>
    <Route exact path={"page1"} element={
        <RequireAuth>
            <Page1 />
        </RequireAuth>
     }/>
    <Route exact path={"page2"} element={
        <RequireAuth>
            <Page2 />
        </RequireAuth>
     }/>
    <Route exact path={"login"} element={<Login />}/>
</Routes>
...

It seemed to work and I was able to protect the protected components. After implementing the authorization process, which I am doing by sending a fetch(...) request to my API (djangorestframework) and getting the result dynamically everytime, I figured out that the protected components got a bit more protective than required. Now, although the server authenticating the request and sending sucessfull response, the protected pages are still locked.

Digging up, I realized that I have used the fetch function which is a Promise and it runs on a separate thread. So, before the fetch could return the result, my component already gets rendered and unmounted.

Code:

RequireAuth.js

...
function RequireAuth({children}) {
    const [auth, setAuth] = useState(false);
    
    fetch(urls.auth, methods.get())
        .then(r => r.json())
        .then(data => {
            setAuth(data)
        })
        .catch(error => {
            console.log(error)
        });
    
    return auth? children : <Navigate to={"../login"}/>;
}
...

I have gone through various technique to solve this, for example,

Code:

RequireAuth.js

...
class RequireAuth extends React.Component {
    constructor(props) {
        super(props);
        this.state = {auth: false}

        fetch(urls.auth, methods.get())
        .then(r => r.json())
        .then(data => {
            this.setState({auth: data})
        })
        .catch(error => {
            console.log(error)
        });
    }

    render() {
        return this.state.auth? this.props.children : <Navigate to={"../login"}/>;
    }
}
...

However, that failed too. Then I looked into,


Finally,

I looked if I can do it using fetching or HttpRequest methods that does not run on different thread, but I afraid, even if it's possible, it can result in bad user experience. Therefore, please help me to solve this fetch authentication issue in React. And, I would also like if there were other ways to implement the dynamic authorization using React and djangorestframework that could do this protective page stuffs more efficiently.

Thank you.

Drew Reese
  • 165,259
  • 14
  • 153
  • 181

1 Answers1

0

Found this answer on StackOverflow as I could not stop searching just because I asked a question. This is very exciting and sad at the same time. I missed one very small part, I thought maybe it was not a very big deal. But guess what?


null is important


Yes, null is what I was missing while returning in my code. So, what I figured is if null is returned, react won't consider it as rendered and the render request will be keep asking, that's what I beleive. So I can simple set the default state of auth to be null and return null if it is null, else check the value of auth.

Code:

index.js

// Unchanged
...
<Router>
    <App />
</Router>
...

App.js

// Unchanged
...
<Routes>
    <Route exact path={"page1"} element={
        <RequireAuth>
            <Page1 />
        </RequireAuth>
     }/>
    <Route exact path={"page2"} element={
        <RequireAuth>
            <Page2 />
        </RequireAuth>
     }/>
    <Route exact path={"login"} element={<Login />}/>
</Routes>
...

RequireAuth.js - this is interesting

...
// Assumption: The server is sending `true` or `false` as the response for authentication for simplicity
function RequireAuth({children}) {
    const [auth, setAuth] = useState(null);  // The spell for the magic

    fetch(urls.auth, methods.get())
        .then(r => r.json())
        .then(data => setAuth(data))
        .catch(error => console.log(error));

    return auth == null?  // The code that did the magic
        null : (auth?
            children : <Navigate to={"/login"}/>)  // Notice, used '/login' instead of 'login' to directly go to login/ authenticate page which is at the root path.
...