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:
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:
And in my parent component I added a default state of:
state = {activeItem: 'home'}
Which is rendering correctly:
But I am still getting the error:
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>