skip to Main Content

I have a user componenet that has a link to a user page/profile. As it is used all over the application to display users, there are some cases where I just want to display the user info, but omit the link to the user’s profile page.

The problem is that, apparently you cannot put an incomplete tag into a JSX expression. Since the Link tag needs to encompass the entire body that will become the link, this doesn’t really work. This seems like a linter bug, because there are plenty of legit cases where this seems legit. The real problem is, how do I cleanly make this work?

Here is a code sample, showing what I would like to achieve:

interface UserAvatar {
  id: number;
  screenName: string;
  displayUserLink: boolean;
}

const UserAvatar = ({ id, screenName, displayUserLink }: UserAvatar) => {
  return (
    {displayUserLink && (<Link to={'/users/' + id} >)}
      <div>{screenName} AND A WHOLE BUNCH OF OTHER STUFF...</div>
    {displayUserLink && (</Link>)}
  );
};

export default UserAvatar;

If fails because the Link tag isn’t closed. What is the best workaround to achieve this goal?

Note that there is a whole lot more being rendered next to the screen name, so this is not a simple string that can be dropped inside the link to render as a simple anchor tag. I just omitted the inner guts to demonstrate the core problem.

Solution

Some people have suggested putting the Screen name and other stuff into a variable and then just using a terinary operator or conditional to manage the output. This is a really clean solution, and after some tweaking, I ended up with:

const UserAvatar = ({id, screenName, displayUserLink}: UserAvatar) => {

const content = (
  <div css={styles.userTag}>
    <div>{screenName} AND A WHOLE BUNCH OF OTHER STUFF...</div>
  </div>
);

return (
    {displayUserLink && (<Link to={'/users/' + id}>{content}</Link>)}
    {!displayUserLink && (content)}
);

};

The subtile point to note is that content is rendered correctly in the link as ‘{content}’, but simply as ‘content’ below.

2

Answers


  1. A cleaner way would be to assign the content to a variable, the optionally include the Link wrappers.

    interface UserAvatar {
      id: number;
      screenName: string;
      displayUserLink: boolean;
    }
    
    const UserAvatar = ({ id, screenName, displayUserLink }: UserAvatar) => {
      const content = <div>{screenName} AND A WHOLE BUNCH OF OTHER STUFF...</div>
    
      return (
        {displayUserLink ? <Link>content</Link> : content }
      );
    };
    
    export default UserAvatar;
    
    Login or Signup to reply.
  2. You can for example conditionally render the component as follows:

    interface UserAvatar {
      id: number;
      screenName: string;
      displayUserLink: boolean;
    }
    
    const UserAvatar = ({ id, screenName, displayUserLink }: UserAvatar) => {
      {displayUserLink ?
      (
     <Link to={'/users/' + id} >
        <div>{screenName} AND A WHOLE BUNCH OF OTHER STUFF..         </div>
     </Link> ) : (<div>{screenName} AND A WHOLE BUNCH OF OTHER STUFF..         </div>)
    };
    
    export default UserAvatar;
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search