Reputation: 1598
I have a NavBar component and I want certain items on the NavBar to be left off when one of the other selections is chosen.
It works somewhat. The problem is that the elements that should disappear require a second click. I want them to disappear from the menu selections with the first click.
I am missing something with useEffect. Am I trying to do something that is a little more complex than my solution is attempting?
Snippet is not working properly
const { React, ReactDOM, PropTypes, w3color } = window
const { useEffect, useState, useRef } = React
const { render } = ReactDOM
import { NavLink } from 'react-router-dom'
const rootNode = document.getElementById('app')
const NavBar = (props) => {
const [menustatus, changeMenusStatus] = useState(null)
const changeContext = async (input) => {
// console.log(input)
changeMenusStatus(input)
}
return (
<>
<h1><ReactLogo />Base React App for Testing Development</h1>
<span>
</span>
<nav>
<NavLink className="navitem" to="/">Home</NavLink>
<NavLink className="navitem" to="/select1">Selection 1</NavLink>
<NavLink className="navitem" to="/select2">Selection 2</NavLink>
{menustatus !== 'restrict' && (
<>
<NavLink className="navitem" to="/select3">Selection 3</NavLink>
<NavLink className="navitem" to="/select4">Selection 4</NavLink>
</>
)
}
<NavLink className="navitem" to="/restrict" onClick={() => changeContext('restrict')}>RESTRICT</NavLink>
<NavLink className="navitem" to="/about">About</NavLink>
</nav>
</>
);
}
const App = () => {
<div className="container">
<NavBar />
</div>
}
render(<App />, rootNode);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.production.min.js"></script>
<div id='app'></div>
The Problem: The issue is that it takes 2 clicks for the NavBar to re-render without the excluded items. I want the excluded items not to appear when the selected link navigates to the proper page.
NavBar.jsx import React, { useState, useEffect } from 'react' import { NavLink } from 'react-router-dom' import './Nav.css' import ReactLogo from '../ReactLogo/ReactLogo'
const Nav = (props) => {
const [menustatus, changeMenusStatus] = useState(null)
const NavContext = React.createContext("")
console.log(`[Nav context] = ${props.navcontext}`)
const changeContext = async (input) => {
console.log(input)
changeMenusStatus(input)
}
useEffect(() => {
if (menustatus) {
console.log(menustatus)
} else {
console.log(`menustatus not set.`)
}
}, [menustatus]);
return (
<>
<h1><ReactLogo />Base React App for Testing Development</h1>
<span>
</span>
<NavContext.Provider value={menustatus}>
<nav>
<NavLink className="navitem" to="/">Home</NavLink>
<NavLink className="navitem" to="/select1">Selection 1</NavLink>
<NavLink className="navitem" to="/select2">Selection 2</NavLink>
<NavContext.Consumer>
{(context) =>
(context ==='restrict') ? null
:
<>
<NavLink className="navitem" to="/select3">Selection 3</NavLink>
<NavLink className="navitem" to="/select4">Selection 4</NavLink>
</>
}
</NavContext.Consumer>
<NavLink className="navitem" to="/restrict" onClick={() => changeContext('restrict')}>RESTRICT</NavLink>
<NavLink className="navitem" to="/about">About</NavLink>
</nav>
</NavContext.Provider>
</>
);
}
export default Nav;
App.js
import React, { Component } from 'react';
import { Routes, Route } from 'react-router-dom';
import './App.css';
import Home from './screens/Home/Home'
import About from './screens/About/About';
import ScreenOne from './screens/ScreenOne/ScreenOne'
import ScreenTwo from './screens/ScreenTwo/ScreenTwo'
import ScreenThree from './screens/ScreenThree/ScreenThree'
import ScreenFour from './screens/ScreenFour/ScreenFour'
import ScreenFive from './screens/ScreenFive/ScreenFive'
import PersonList from './components/PersonList/PersonList'
class App extends Component {
state = {people: []}
render() {
return (
<div className="App">
<Routes>
<Route path="/" element={<Home navcontext="one" />} />
<Route path="/about" element={<About navcontext="one" />} />
<Route path="/select1" element={<ScreenOne navcontext="one" />} />
<Route path="/select2" element={<ScreenTwo navcontext="two" />} />
<Route path="/select3" element={<ScreenThree navcontext="three" />} />
<Route path="/select4" element={<ScreenFour navcontext="four" />} />
<Route path="/restrict" element={<ScreenFive navcontext="restrict" />} />
</Routes>
<PersonList people={this.state.people} />
</div>
);
}
}
export default App;
Upvotes: 2
Views: 734
Reputation: 202628
I think the issue is that you are declaring the NavContext
context each render cycle inside the Nav
component. If you need to use a React Context then be sure to declare it outside any React component.
I don't see the point in using a React Context here though when you can just conditionally render the menu items on the menustatus
state locally.
Example:
const Nav = (props) => {
const [menustatus, changeMenusStatus] = useState(null);
const changeContext = (input) => {
console.log(input);
changeMenusStatus(input);
};
useEffect(() => {
if (menustatus) {
console.log(menustatus);
} else {
console.log(`menustatus not set.`);
}
}, [menustatus]);
return (
<>
<h1>
<ReactLogo />
Base React App for Testing Development
</h1>
<nav>
<NavLink className="navitem" to="/">Home</NavLink>
<NavLink className="navitem" to="/select1">Selection 1</NavLink>
<NavLink className="navitem" to="/select2">Selection 2</NavLink>
{menustatus !== 'restrict' && (
<>
<NavLink className="navitem" to="/select3">Selection 3</NavLink>
<NavLink className="navitem" to="/select4">Selection 4</NavLink>
</>
)}
<NavLink className="navitem" to="/restrict" onClick={() => changeContext('restrict')}>RESTRICT</NavLink>
<NavLink className="navitem" to="/about">About</NavLink>
</nav>
</>
);
}
Upvotes: 1