'How to use ProtectedRoute (RequireAuth) with fetch(url, method) in React (React Router v6)?
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,
- Fetch data and then render it to dom React
- Web Fetch API (waiting the fetch to complete and then executed the next instruction)
- Trying to implement a SIMPLE promise in Reactjs
- How to return data from promise
- How to get data returned from fetch() promise?
- How to finish all fetch before executing next function in React?
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.
Solution 1:[1]
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.
...
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Mohammed Julfikar Ali Mahbub |
