3

Getting Error: Guard(...): Nothing was returned from render. This usually means a return statement is missing.

I want to call the API in my guard component before returning the element to Route Element to check if the logged in user have access to specific route or not.

For that, I have implemented a guard. Here's the route file code.

export default function Router() {
    return (
        <Routes>
            <Route path="organizations">
                <Route path="list" element={<Guard><ListOrganization /></Guard>} />
        </Routes >
    );
}

Guard component code

const Guard = (props) => {
    fetch('https://apiToCheckPermission.com')
    .then(response => {
        if (response.isPermission) {
            return props.children;
        }
    })
};

export default Guard;

It's kind of a thing that, I want to implement async route element. But React is throwing error if I don't immediately return the element from Guard.

Can anyone please tell How to solve this error?

2 Answers 2

2

If Guard is supposed to be a React component then it needs to always return valid JSX. The Guard currently returns nothing. I.E. it would need to return something from the Promise chain and then return that from the function body.

To resolve use some local state to hold a confirmed/verified permission value and conditionally render the children prop or a fallback. A typical route protection implementation will wait to confirm a user's access then render either the children or redirect to the login page with the location being accessed so the user can be redirected back after authenticating.

Example:

const Guard = ({ children }) => {
  const location = useLocation();

  const [hasPermission, setHasPermission] = React.useState(); // <-- initially undefined

  React.useEffect(() => {
    );
    fetch('https://apiToCheckPermission.com')
      .then(response => {
        setHasPermission(response.isPermission);
      });
  }, []);

  if (hasPermission === undefined) {
    return null; // or loading indicator, spinner, etc
  }

  return hasPermission
    ? children
    : <Navigate to="/login" replace state={{ from: location }} />;
};
Sign up to request clarification or add additional context in comments.

2 Comments

But that's not the async routing. I am returning another component until I got the response from API. I don't want to show another component to user.
@ChrisT What do you want to display to the user while the asynchronous code is running? In my example here I'm returning null to not render anything until the permission check completes. I'm not sure what you mean by "async routing", route matching and rendering is completely synchronous. React component rendering is completely synchronous.
2

Try to define a state to handle the permission and useEffect to load the data:

const Guard = (props) => {
  const [hasPermission, setHasPermission] = useState(false);

  useEffect(() => {
    const fetchPermission = async () => {
        const response = await fetch('https://apiToCheckPermission.com');
        setHasPermission(response.isPermission);
    }
    
    fetchPermission().catch(console.error);
  }, []);

  if (!hasPermission) return <>Unauthorized</>;

  return props.children;
};

export default Guard;

3 Comments

Are you importing useState and useEffect?
It depends on the structure of the response. Try to set setHasPermission(response.isPermission);
The useEffect hook callback absolutely cannot be an async function. This will implicitly return a Promise object that React will try to use as the useEffect hook's cleanup function. Just abstract the asynchronous callback into an inner function that is called in the callback, or declare it outside the useEffect hook and call in the callback.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.