0

I am using semantic-react-ui to make a menu. To leverage the active state functionality. When you're on a link it gets focus or underline in this case. However with my implementation I am passing it down the function to a child component as props. See below...

class DesktopContainer extends Component {
 state = {}

 handleItemClick(event) {
  var { name } = event.target;
  this.setState({
   activeItem: name
  })
 }


 render() {
  const { GenericHeadingComponent, children, getWidth, isLoggedIn, logOutUser } = this.props

  const { fixed, activeItem } = this.state

  return (
   <Responsive getWidth={getWidth} minWidth={Responsive.onlyTablet.minWidth}>

        <GenericIsUserLoggedInLink
         isHomeButton={true}
         key="1"
         name='home'
         active={activeItem === 'home'}
         handleItemClick={this.handleItemClick} /* is this correct */
         mobile={false}
        />

        <GenericIsUserLoggedInLink
         isLoggedIn={isLoggedIn}
         route="/profile"
         anchorText="Profile"
         key="2"
         name='profile'
         active={activeItem === 'profile'}
         handleItemClick={this.handleItemClick} /* is this correct */
         mobile={false}
        />

        <GenericIsUserLoggedInLink
         isLoggedIn={isLoggedIn}
         route="/dashboard"
         anchorText="Dashboard"
         key="3"
         name='dashboard'
         active={activeItem === 'dashboard'}
         handleItemClick={this.handleItemClick}  /* is this correct */
         mobile={false}

        />


        <Menu.Item position='right'>
         <Button inverted={!fixed}>
          <GenericIsUserLoggedInLink
           route="/login"
           isLoggedIn={isLoggedIn}
           logOutUser={logOutUser}
           key="4" />
         </Button>
         <Button inverted={!fixed} primary={fixed} style={{ marginLeft: '0.5em' }}>
          <Link href="/register">
           <a>Register</a>
          </Link>
         </Button>
        </Menu.Item>
       </Container>
      </Menu>
      <GenericHeadingComponent />
     </Segment>
    </Visibility>

    {children}
   </Responsive>
  )
 }
}

DesktopContainer.propTypes = {
 children: PropTypes.node,
}


LayoutComponent.propTypes = {
 children: PropTypes.node,
}
     var comparator;
const GenericIsUserLoggedInLink = React.memo(({ isHomeButton, isLoggedIn, logOutUser, route, anchorText, mobile, active, handleItemClick }) => {
 comparator = (prevProps, nextProps) => {
  if (prevProps.isHomeButton !== nextProps.setProps.isHomeButton) {
   return true;
  }
  if (prevProps.isLoggedIn !== nextProps.setProps.route) {
   return true;
  }
  if (prevProps.anchorText !== nextProps.setProps.anchorText) {
   return true;
  }
  if (prevProps.active !== nextProps.setProps.active) {
   return true;
  }
  return false;
 }
if (isHomeButton) {
 return <Menu.Item active={active} onClick={() => handleItemClick}><Link href="/"><a>Home</a></Link></Menu.Item>
}
 if (isLoggedIn) {
  if (anchorText === undefined) {
   return <Link href="/"><a onClick={() => logOutUser()}>Log out!</a></Link>
  }
  else if (anchorText && mobile) {
   console.log("active 1 ", active);

   return <Menu.Item active={active}><Link href={route}><a >{anchorText}</a></Link></Menu.Item>
  }
  else if ((anchorText) && (!(mobile))) {
   console.log("mobile 2 ", mobile);
   return <Menu.Item active={active} onClick={() => handleItemClick}><Link href={route}><a >{anchorText}</a></Link></Menu.Item>
  }

  else if (anchorText) {
   return <Link href={route}><a >{anchorText}</a></Link>
  }
 } else {
  if (route === "/login") {
   return <Link href="/login"><a >Log in!</a></Link>
  }
  return  null
 }
}, comparator);

Should I move the state down into the HOC? I am not getting errors so I'm stumped.

UPDATE 10/25/2019

Decided to just add the latest feedback(error) from console and updated code:

Now when I click the /profile link/route I get this error:

index.js:1 Warning: validateDOMNesting(...): <a> cannot appear as a descendant of <a>.
    in a (created by Link)
    in Link
    in a (created by MenuItem)
    in MenuItem
    in Unknown (created by DesktopContainer)
    in div (created by Container)
    in Container (created by DesktopContainer)
    in div (created by Menu)
    in Menu (created by DesktopContainer)
    in div (created by Segment)
    in Segment (created by DesktopContainer)
    in div (created by Visibility)
    in RefFindNode (created by Ref)
    in Ref (created by Visibility)
    in Visibility (created by DesktopContainer)
    in div (created by Responsive)
    in Responsive (created by DesktopContainer)
    in DesktopContainer (created by LayoutComponent)
    in LayoutComponent (created by Connect(LayoutComponent))
    in Connect(LayoutComponent) (created by ProfilePage)
    in ProfilePage (created by Profile)
    in Profile (created by Connect(Profile))
    in Connect(Profile) (created by Auth)
    in Auth (created by MyApp)
    in PersistGate (created by MyApp)
    in Provider (created by MyApp)
    in MyApp (created by AppWithRedux)
    in AppWithRedux
    in Suspense (created by AppContainer)
    in Container (created by AppContainer)
    in AppContainer

This is the HOC:

var comparator;
const GenericIsUserLoggedInLink = React.memo(({ isHomeButton, isLoggedIn, logOutUser, route, anchorText, mobile,  activeItem, name, active, handleItemClick }) => {
 comparator = (prevProps, nextProps) => {
  if (prevProps.isHomeButton !== nextProps.setProps.isHomeButton) {
   return true;
  }
  if (prevProps.isLoggedIn !== nextProps.setProps.isLoggedIn) {
   return true;
  }
  if (prevProps.anchorText !== nextProps.setProps.anchorText) {
   return true;
  }
  if (prevProps.active !== nextProps.setProps.active) {
   return true;
  }
  if (prevProps.mobile !== nextProps.setProps.mobile) {
   return true;
  }
  if (prevProps.activeItem !== nextProps.setProps.activeItem) {
   return true;
  }
  if (prevProps.active !== nextProps.setProps.active) {
   return true;
  }
  return false;
 }
if (isHomeButton) {
 console.log("active ", active);
 return <Menu.Item name={name} active={activeItem === name} onClick={handleItemClick}><Link href="/"><a>Home</a></Link></Menu.Item>
}
 if (isLoggedIn) {
  if (anchorText === undefined) {
   return <Link href="/"><a onClick={() => logOutUser()}>Log out!</a></Link>
  }
  else if (anchorText && mobile) {
   console.log("active 1 ", active);

   console.log("mobile 1 ", mobile);
   return <Menu.Item active={active}><Link href={route}><a >{anchorText}</a></Link></Menu.Item>
  }
  else if ((anchorText) && (!(mobile))) {
   console.log("mobile 2 ", mobile);
   return <Menu.Item name={name} active={activeItem === name} onClick={handleItemClick}><Link href={route}><a >{anchorText}</a></Link></Menu.Item>
  }

  else if (anchorText) {
   return <Link href={route}><a >{anchorText}</a></Link>
  }
 } else {
  if (route === "/login") {
   return <Link href="/login"><a >Log in!</a></Link>
  }
  return  null
 }
}, comparator);

This is the class component:

    class DesktopContainer extends Component {
     state = {}

     handleItemClick = (e, { name }) => this.setState({ activeItem: name })

     render() {
      const { GenericHeadingComponent, children, getWidth, isLoggedIn, logOutUser } = this.props

      const { fixed, activeItem } = this.state

    return (
   <Responsive getWidth={getWidth} minWidth={Responsive.onlyTablet.minWidth}>
    <Visibility
     once={false}
     onBottomPassed={this.showFixedMenu}
     onBottomPassedReverse={this.hideFixedMenu}
    >
     <Segment
      inverted
      textAlign='center'
      style={{ minHeight: 700, padding: '1em 0em' }}
      vertical
     >
      <Menu
       fixed={fixed ? 'top' : null}
       inverted={!fixed}
       pointing={!fixed}
       secondary={!fixed}
       size='large'
      >
       <Container>

        <GenericIsUserLoggedInLink
         isHomeButton={true}
         key="1"
         name='home'
         active={activeItem === 'home'}
         handleItemClick={this.handleItemClick}
         mobile={false}
        />

        <GenericIsUserLoggedInLink
         isLoggedIn={isLoggedIn}
         route="/profile"
         anchorText="Profile"
         key="2"
         name='profile'
         active={activeItem === 'profile'}
         handleItemClick={this.handleItemClick}
         mobile={false}
        />

        <GenericIsUserLoggedInLink
         isLoggedIn={isLoggedIn}
         route="/dashboard"
         anchorText="Dashboard"
         key="3"
         name='dashboard'
         active={activeItem === 'dashboard'}
         handleItemClick={this.handleItemClick}
         mobile={false}

        />

        <Menu.Item position='right'>
         <Button inverted={!fixed}>
          <GenericIsUserLoggedInLink
           route="/login"
           isLoggedIn={isLoggedIn}
           logOutUser={logOutUser}
           key="4" />
         </Button>
         <Button inverted={!fixed} primary={fixed} style={{ marginLeft: '0.5em' }}>
          <Link href="/register">
           <a>Register</a>
          </Link>
         </Button>
        </Menu.Item>
       </Container>
      </Menu>
      <GenericHeadingComponent />
     </Segment>
    </Visibility>

    {children}
   </Responsive>
  )
 }
}

UPDATE 10/25/19 9:21PM EST

I updated my my HOC:

var comparator;
const GenericIsUserLoggedInLink = React.memo(({ isHomeButton, isLoggedIn, logOutUser, route, anchorText, mobile,  activeItem, name, handleItemClick }) => {

 comparator = (prevProps, nextProps) => {

  if (prevProps.isHomeButton !== nextProps.setProps.isHomeButton) {
   return true;
  }
  if (prevProps.isLoggedIn !== nextProps.setProps.isLoggedIn) {
   return true;
  }
  if (prevProps.mobile !== nextProps.setProps.mobile) {
   return true;
  }
  if (prevProps.active !== nextProps.setProps.active) {
   return true;
  }
  return false;
 }
if (isHomeButton) {
 return <Link href="/"><Menu.Item name={name} active={activeItem === name} onClick={()=>handleItemClick(name)}></Menu.Item></Link>
}
 if (isLoggedIn) {
  if (anchorText === undefined) {
   return <Link href="/"><a onClick={() => logOutUser()}>Log out!</a></Link>
  }
  else if (anchorText && mobile) {

   return <Link href={route}><Menu.Item name={name} active={activeItem === name}>{anchorText}</Menu.Item></Link>
  }
  else if ((!(mobile))) {
   console.log("mobile 2 ", mobile);
   return <Link href={route}><Menu.Item name={name} active={activeItem === name} onClick={() => handleItemClick(name)}></Menu.Item></Link>
  }

  else if (anchorText) {
   return <Link href={route}><a>{anchorText}</a></Link>
  }
 } else {
  if (route === "/login") {
   return <Link href="/login"><a>Log in!</a></Link>
  }
  return  null
 }
}, comparator);

This is my component:

class DesktopContainer extends Component {
 state = {}

 hideFixedMenu = () => this.setState({ fixed: false })
 showFixedMenu = () => this.setState({ fixed: true })
 handleItemClick = (e, { name }) => this.setState({ activeItem: name })


 logOutUser = () => {
  const { logOutUser } = this.props
  logOutUser()
 }

 render() {
  const { GenericHeadingComponent, children, getWidth, isLoggedIn, logOutUser } = this.props


  const { fixed, activeItem } = this.state

  return (
   <Responsive getWidth={getWidth} minWidth={Responsive.onlyTablet.minWidth}>
    <Visibility
     once={false}
     onBottomPassed={this.showFixedMenu}
     onBottomPassedReverse={this.hideFixedMenu}
    >
     <Segment
      inverted
      textAlign='center'
      style={{ minHeight: 700, padding: '1em 0em' }}
      vertical
     >
      <Menu
       fixed={fixed ? 'top' : null}
       inverted={!fixed}
       pointing={!fixed}
       secondary={!fixed}
       size='large'
      >
       <Container>

        <GenericIsUserLoggedInLink
         isHomeButton={true}
         key="1"
         name='home'
         activeItem={activeItem}
         handleItemClick={this.handleItemClick}
         mobile={false}
        />

        <GenericIsUserLoggedInLink
         isLoggedIn={isLoggedIn}
         route="/profile"
         anchorText="Profile"
         key="2"
         name='profile'
         activeItem={activeItem}
         handleItemClick={this.handleItemClick}
         mobile={false}
        />

        <GenericIsUserLoggedInLink
         isLoggedIn={isLoggedIn}
         route="/dashboard"
         anchorText="Dashboard"
         key="3"
         name='dashboard'
         activeItem={activeItem}
         handleItemClick={this.handleItemClick}
         mobile={false}
        />


        <Menu.Item position='right'>
         <Button inverted={!fixed}>
          <GenericIsUserLoggedInLink
           route="/login"
           isLoggedIn={isLoggedIn}
           logOutUser={logOutUser}
           key="4" />
         </Button>
         <Button inverted={!fixed} primary={fixed} style={{ marginLeft: '0.5em' }}>
          <Link href="/register">
           <a>Register</a>
          </Link>
         </Button>
        </Menu.Item>
       </Container>
      </Menu>
      <GenericHeadingComponent />
     </Segment>
    </Visibility>

    {children}
   </Responsive>
  )
 }
}

But now getting this error:

enter image description here

How can that be if I am passing the function in correctly?

        <GenericIsUserLoggedInLink
         isHomeButton={true}
         key="1"
         name='home'
         activeItem={activeItem}
         handleItemClick={this.handleItemClick} /* no ? */
         mobile={false}
        />

And using it correctly?

if (isHomeButton) {
 return <Link href="/"><Menu.Item name={name} active={activeItem === name} onClick={()=>handleItemClick(name)}></Menu.Item></Link>
}

UPDATE 10/26/19

So I know this is working because of the console out put...

  else if ((!(mobile))) {
   console.log("name 1", name);
   console.log("activeItem 1", activeItem);
   return <Link href={route}><Menu.Item name={name} active={activeItem === name} onClick={handleItemClick}></Menu.Item></Link>
  }

here:

enter image description here

And in my parent component I added a default state of:

 state = {activeItem: 'home'}

Which is rendering correctly:

enter image description here

But I am still getting the error:

enter image description here

And for good measure here are my components as they are in the Parent:

        <GenericIsUserLoggedInLink
         isHomeButton={true}
         key="1"
         name='home'
         activeItem={activeItem}
         handleItemClick={this.handleItemClick}
         mobile={false}
        />

        <GenericIsUserLoggedInLink
         isLoggedIn={isLoggedIn}
         route="/profile"
         anchorText="Profile"
         key="2"
         name='profile'
         activeItem={activeItem}
         handleItemClick={this.handleItemClick}
         mobile={false}
        />

        <GenericIsUserLoggedInLink
         isLoggedIn={isLoggedIn}
         route="/dashboard"
         anchorText="Dashboard"
         key="3"
         name='dashboard'
         activeItem={activeItem}
         handleItemClick={this.handleItemClick}
         mobile={false}
        />

Should I be passing in the parameter in the child component?

 return <Link href="/"><Menu.Item name={name} active={activeItem === name} onClick={()=>handleItemClick(name)}></Menu.Item></Link>
Antonio Pavicevac-Ortiz
  • 7,239
  • 17
  • 68
  • 141

2 Answers2

1

First of all your handleClick will cause an error when setting a state because of the context of this. So i suggest you either bind it in constructor or change it to an arrow function like this -

handleItemClick = (e) => {}

Now, I have used semantic-ui-react for couple of projects and there click events runs a bit differently. According to the docs -

Called on click. When passed, the component will render as an `a`
tag by default instead of a `div`.

onClick(event: SyntheticEvent, data: object)
event
React's original SyntheticEvent.
data
All props.

So change your onClick to onClick={handleItemClick} then

 handleItemClick = (e, { name }) => this.setState({ activeItem: name })

I hope this helps you.

UPDATE: Your latest error is because of <a> tags inside Link. Link from react-router is like an tag and we know

<a href="1">
    <a href="2"></a>
</a>

is invalid HTML. You can refer to this question for more information Link cannot appear as a descendant of a link.

And to solve your errors simply remove tags inside tags.

UPDATE: -

The latest error is because you are passing name in your handleItemClick

if (isHomeButton) {
 return <Link href="/"><Menu.Item name={name} active={activeItem === name} onClick={handleItemClick}></Menu.Item></Link> //change the onClick here//
}
Atin Singh
  • 3,624
  • 2
  • 18
  • 25
1

Can you try binding your method in constructor as

class DesktopContainer extends Component {
  constructor(props) {
    super(props);
    this.state = {};
    this.handleItemClick = this.handleItemClick.bind(this);
  }
  // remaining code

}

Hope it helps

ibtsam
  • 1,620
  • 1
  • 10
  • 10