Context API bringing state up to higher component - javascript

I'm trying to get my nav bar to change depending on which page the app is on without having to create individual nav components for each page. I want the nav bar to change once the user goes to a new 'projects' page. The way I would do this is to conditionally render the nav bar elements depending on the url parameter. Once the user clicks to go to the project page, there will be a url parameter such as localhost/project/movie. However, the nav bar component does not have access to the match.params.projectId, only the project page component has access to it since it's within a Route component from react-router. Therefore, I want to make a global state that stores the url parameter so that I could use it within the navbar component to conditionally render the elements of that component.
Highest level App component
const App = () => {
return (
<div className="app">
<Header/>
<Switch>
<Route exact path="/" component={Home}/>
<Route exact path="/project/:projectId" component={ProjectOverview}/>
</Switch>
</div>
);
}
Lower level Project page component
const ProjectOverview = ({ match }) => {
useEffect(() => {
window.scrollTo(0, 650);
}, []);
let project = {};
if(match.params.projectId === 'movie'){
project = {
p: 'Browse your favorite Movies, TV shows, and actors. Search for specific movies, shows or actors by date, rating, region and other categories. Browse the latest and greatest films and to find information about its actors, crew, and reviews. Rate and favorite Movies, TV shows and actors while having access to them through a user account. Login / Authentication.',
img: 'http://www.technologies.com/images/cloud-summary.png',
gif: 'https://giphy.com/media/mUgG6FZuWN9V1/giphy.gif',
list: ['React', 'Redux', 'Hooks', 'TMDB API', 'Sass', 'React Router']
}
} else if(match.params.projectId === 'ecommerce'){
project = {
p: 'Something else',
img: 'http://www.technologies.com/images/cloud-summary.png',
gif: 'https://giphy.com/media/IgASZuWN9V1/giphy.gif',
list: ['React', 'Redux', 'Hooks', 'TMDB API', 'Sass', 'React Router']
}
}
return(
I know the provider has to wrap around the header(navbar) component within the app component but the state value that is needed (match.params.projectId) can only be found within the project page component. I'm wondering how I could get the value for the provider to be whatever the url parameter is within the project page component. I will try to further clarify my question if it seems too confusing for anyone. Thank you

I don't know if it is the best solution, but in an application where I had to change the nav's title depending on the route I did the following:
function App() {
const [title, setTitle] = useState('');
return <Router>
<Navbar title={title} />
<Switch>
<Route path="/product/:id" exact render={props => <ProductPage {...props} setTitle={setTitle} />} />
</Switch>
</Router>
}
And in the ProductComponent:
function ProductionComponet({setTitle, match}){
useEffect(()=>{
setTitle(`product ${match.params.id}!`)
},[])
}

Related

react.js & django, useParams unable to navigate to the page

I am current building a react app with django, I am trying to navigate from the HomePage to the DataPage with corrsponding id. However, it return Page not found error. I am using react-router-dom v6.
Using the URLconf defined in robot.urls, Django tried these URL patterns, in this order:
admin/
api/
api-auth/
homepage
homepage/data
The current path, homepage/data/54, didn’t match any of these.
Here is my App.js
export default class App extends Component {
constructor(props) {
super(props);
}
renderHomePage() {
return (
<HomePage />
);
}
render() {
return (
<BrowserRouter>
<Routes>
<Route exact path='homepage/' element={this.renderHomePage()} />
<Route path='homepage/data/:id' element={<DataPage />} />
</Routes>
</BrowserRouter>
)
}
}
const appDiv = document.getElementById("app");
render(<App />, appDiv);
And I want to navigate to the DataPage below:
const EmtpyGrid = theme => ({
Grid: { ... }
});
function DataPage(props) {
const { classes } = props;
const { id } = useParams();
return (
<div>
... some material ui components ...
<div/>
)
};
DataPage.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(EmtpyGrid)(DataPage);
I was thinking whether I need configure my url.py in frontend as well, and I need to define a designated value for {id} returned from the materialui component first. Perhaps I need a button or <Link><Link/> for the navigation instead of just simply typing in the url? Still, no luck after many attempts. I am so confused right now.
If you know what is wrong with my code, please feel free to leave a comment. Thanks
After many tries and checking documents, I don't really need to configure my urls.py. I only things that I am missing is to put a parameter in my naviagate() from onRowClick={((rowData, event) => {navigate('data/');})} to onRowClick={((rowData, event) => {let id = event.sample_ID; navigate('data/' + id)})}; I was thinking the problem too complicated.
Thanks you guys for sharing!

Cannot code nested routes with react-router-dom 5.2.0?

I'm trying to access to sub components with nested routing in React using react-router-dom 5.2.0.
Here you can find a CodeSandbox link of the project: https://codesandbox.io/s/nested-routes-8c7wq?file=/src/App.js
First, let me show you the tree structure of my pages.
Home
About
Organisations
MainContent
SecondContent
Routes for main pages (Home, About, Organizations) are in src/routes folder.
So their link look like:
https://localhost:3000/ (for Home)
https://localhost:3000/about (for About)
https://localhost:3000/organizations (for Organizations)
At this point, everything is okay for me. The problem is with the nesting part..
On Organizations page, I have to be able to switch between MainContent and SecondContent.
So links into that page must look like this:
https://8c7wq.csb.app/organizations/maincontent
https://8c7wq.csb.app/organizations/secondcontent
But I cannot print any of those contents...
If you want to build nested paths you should use the path and url from the current route match and build the nested routes and links from there.
src/pages/Orgainizations
import { Switch, Route, useRouteMatch } from "react-router-dom";
const Organizations = (props) => {
const { path, url } = useRouteMatch(); // <-- get current path and url
const [value, setValue] = useState("");
const menuLinks = {
data: [
{
id: 0, // <-- fix React key issue in NavBar
title: "StackOverflow",
to: `${url}/maincontent` // <-- build nested link
},
{
id: 1, // <-- fix React key issue in NavBar
title: "BitBucket",
to: `${url}/secondcontent` // <-- build nested link
}
]
};
return (
<div>
<NavBar changeValue={(value) => setValue(value)} menuLinks={menuLinks} />
<div>
<Switch>
<Route
render={() => <MainContent title={value} />} // *
exact
path={`${path}/maincontent`} // <-- build nested path
strict
/>
<Route
render={() => <SecondContent title={value} />} // *
exact
path={`${path}/secondcontent`} // <-- build nested path
strict
/>
</Switch>
</div>
</div>
);
};
* Notice I converted to rendering route components via the render prop as opposed to the component prop. When you use anonymous functions to render a component it is created each render cycle, i.e. it will remount the component each time. The render prop does not do this. See render methods for more information.
NavBar
Fix the to prop value to render the link correctly, remove the leading "/" from the path and render the direct link.to value.
Change
to={`/${props.link.to}`}
to
to={props.link.to}
Demo

React/Redux/React Router - Only call API when route is first visited or the filter is changed

Is there a way to only run props.fetchTransactions() under the following conditions:
When the route is first navigated to
If props.filter is changed
At the moment, it runs every time the route is navigated to because <TransactionListContainer /> is unmounted and re-mounted by react router. This creates an unnecessary call to the API as it just re-fetches the exact same result set that is already in my Redux store.
The only way I can think of doing this is by lifting the useEffect hook up into the root <App/> so it's outside of the router, but i'm unsure if that's a good way to go because:
It will call the API when <App/> is loaded, even if the user is viewing a different route
<App/> will quickly become cluttered if I need to repeat this for other containers
// App root
const App = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={ () => <TransactionListContainer /> } />
...other routes...
</Switch>
</BrowserRouter>
);
}
// Container
const TransactionListContainer = (props) => {
useEffect(() => {
// This should only happen when the route is first visited or if props.filter has changed
props.fetchTransactions(props.filter.fields, props.filter.pagination);
},[props.filter]);
// The transactions are passed to this container
// by redux: props.transactions. I haven't included
// any code that I believe isn't relevant
// to the question.
}
You can provide a flag variable indicating if the transaction data was cached previously (is available in redux store).
const TransactionListContainer = (props) => {
useEffect(() => {
if(!props.isCached){
props.fetchTransactions(props.filter.fields, props.filter.pagination);
}
},[props.filter, props.isCached]);
}
function mapStateToProps(state) {
const isCached = state.transactions.length !== 0
return { isCached }
}
export default connect(mapStateToProps)(TransactionListContainer)

React-redux state changes but no re-renders

I currently have a single page app that logs a user in and after login should re-render to the appropriate page. On dev tools I see the state change but I have to manually refresh the page. The page re-render on the basis of seeing a current user exists. If there is a current user it should return a different component. Here is the code snippet below.
So far ive double checked to make sure my reducer is not mutating the state and is in fact returning the correct newState. My state is also normalized correctly and the component below is correctly watching the slice of state that changes.
class Greeting extends React.Component {
constructor(props) {
super(props)
debugger
}
render () {
debugger
const sessionLinks = () => (
<div className="login-signup">
<div className="navhead">
<div className="butterflyr">butterflyr</div>
<img className="butterfree" src={window.butterFree}/>
<Route exact path="/" component={LogInFormContainer}/>
</div>
<SignUpFormContainer/>
</div>
);
const homePage = () => (
<hgroup className="header-group">
<NavBar currentUser={this.props.currentUser} logout={this.props.logout}></NavBar>
<Route exact path="/" component={ForFun}/>
</hgroup>
)
return (
<div>{this.props.currentUser ? homePage() : sessionLinks()}</div>
)
}
}

Programmatically create PrivateRoutes on Gatsby

I need to create a client's only Blog. I followed gatsby docs to create a site with user authentication, but i need to programmatically create PrivateRoutes based on markdown files.
By now i'm using this component to render PrivateRoutes and they work, but how do I create a PrivateRoute for a dynamically created page if a component doesn't exist. Pages are created at build time with gatsby onCreatePage API but it only uses a template component.
<Layout>
<Router>
<PrivateRoute path="/app/profile" component={Profile} />
<PrivateRoute path="/app/contact" component={ContactPage} />
<Login path="/app/login" />
</Router>
</Layout>
)
Static Pages Routes are created like this:
exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
// page.matchPath is a special key that's used for matching pages
// only on the client.
if (page.path.match(/^\/app/)) {
page.matchPath = "/app/*"
// Update the page.
createPage(page)
}
}
And this is what I'm trying to do for blog posts:
exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
// page.matchPath is a special key that's used for matching pages
// only on the client.
if (page.path.match(/^\/blog/)) {
page.matchPath = "/blog/*"
// Update the page.
createPage(page)
}
}

Categories

Resources