Change parent component variable from child component when using routing in react - reactjs

I am trying to update parent components title from child components url. But the child components are loaded as navigation
Here is my Sandbox
https://codesandbox.io/s/react-typescript-forked-z1ijxi
Here is my layout.tsx
import { createContext, useContext, useState } from "react";
import { Outlet, Link } from "react-router-dom";
const Layout = () => {
const title = "";
return (
<>
<h2>{title}</h2>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/blogs">Blogs</Link>
</li>
<li>
<Link to="/contact">Contact</Link>
</li>
</ul>
</nav>
<Outlet />
</>
);
};
export default Layout;
Here I need to change the {title} when each of the child component loads
I tried to follow this answer . But I was not able to accomplish that.. Sorry very new in React

I would recommend using React Redux in your project. This library adds a variable namespace that is available to every component. From this global namespace, you could define a title variable that is accessible to the Layout component and editable by any other component no matter where it is.
Redux takes some time to learn, but it's very useful in all kinds of projects. This article has more information on the concept of Redux, though the exact implementation is a little outdated.

Related

Typical differences between routers and links in NextJS

I am new to NextJS. I would like to know the typical variations and use cases between next/router and next/link.
Which one should I use in various scenarios? Which does what? For instance, if I want to load shared components or navigate among pages which are server-side rendered. Also what is the difference if I call a page with a normal 'a' tag or use a link/router to achieve the same.
import { useRouter } from 'next/router'
function ActiveLink({ children, href }) {
const router = useRouter()
const style = {
marginRight: 10,
color: router.pathname === href ? 'red' : 'black',
}
const handleClick = (e) => {
e.preventDefault()
router.push(href)
}
return (
<a href={href} onClick={handleClick} style={style}>
{children}
</a>
)
}
export default ActiveLink
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link href="/">
<a>Home</a>
</Link>
</li>
<li>
<Link href="/about">
<a>About Us</a>
</Link>
</li>
</ul>
)
}
export default Home
what is the difference if I call a page with a normal 'a' tag or use a link/router to achieve the same?
The main difference between a normal <a> tag and next/link or next/router is that the latter two are used for client-side transitions. So a normal <a> tag will load/navigate to the page with an http request, while router and link with load the page client-side.
You can use next/router (inside a function in your component) when next/link is not enough, for example if you need to do some operations before render the new component.
So both router and link has similar behaviours, you can choose, based on what your app needs to do, which one to use.
Both, will run data fetching methods (getServerSideProps, getStaticProps, and getInitialProps)
Link is just an abstraction for the router, it is a lot easier to use the Link component than to manually construct the link every time.

Hide full screen navigation when BrowserRouter Link is clicked

I'm setting up a navigation for a react app that I'm currently working on.
I have a full screen navigation which overlays the website when a button in the header is clicked.
I've used useState to toggle the overlay on and off which is working great but I need to find a way to automatically hide the navigation overlay when a page link within the nav is clicked.
This is my Nav component:
import React, { useState } from 'react';
import { NavLink as Link } from 'react-router-dom';
function Navigation(){
const[showMenu, setShowMenu] = useState(false)
let menu
if(showMenu){
menu =
<nav>
<ul>
<Link to="/" exact activeStyle={{color: 'black'}}><li>Home</li></Link>
<Link to="/about"><li>About</li></Link>
<Link to="/work"><li>Work</li></Link>
<Link to="/hire"><li>Hire Me</li></Link>
</ul>
</nav>
}
return(
<>
<button className="nav-btn" onClick={() => setShowMenu(!showMenu)}>Menu</button>
{menu}
</>
);
}
export default Navigation;
I've tried adding onClick to the links like this:
<Link onClick={() => useState(false)} exact activeStyle={{color: 'black'}} to="/">
<li>Home</li>
</Link>
but I just get a compiler error "React Hook "useState" cannot be called inside a callback." So That's me out of ideas.
Any guidance would be greatly appreciated, as I'm learning React as I go.
useState has to be executed from a method. Simply create a method and set useState inside it.
function navigate(){
useState(false)
}
. . . . . . .
<Link onClick={navigate} exact activeStyle={{color: 'black'}} to="/">
<li>Home</li>
</Link>
Managed to figure it out.
I was right with the OnClick listener, but not quite there with the rest.
<Link onClick={() => setShowMenu(false)} to="/" exact activeStyle={{color: 'black'}}>
<li>Home</li>
</Link>

How to prevent re-render of react-router-dom Link component?

I am trying to limit the amounts of components that re-render on my app everytime the user clicks something. Given that the Sidebar renders regardless of which page the user is on, it seems to make sense to wrap it inside a React.memo function. This works well and the Sidebar component itself does not seem to re-render. However, the <Link> elements, which I import from react-router-dom do re-render, as do the SidebarAuthButtons and the SidebarCreateButton.
What can I do to prevent this behavior?
import React, { memo } from "react";
import { Link } from "react-router-dom";
import {
SidebarContainer,
SidebarLogo,
SidebarNav,
SidebarMenu,
SidebarListItem,
SidebarButton,
} from "../styles/SidebarStyles";
function Sidebar({ auth }) {
const SidebarAuthButtons = (
<div>
<SidebarButton>
<Link to="/login">Log In</Link>
</SidebarButton>
<SidebarButton outlined={true}>
<Link to="/register">Create Account</Link>
</SidebarButton>
</div>
);
const SidebarCreateButton = (
<SidebarButton>
<Link to="#">Create</Link>
</SidebarButton>
);
return (
<SidebarContainer>
<SidebarLogo>React Project</SidebarLogo>
<SidebarNav>
<SidebarMenu>
<SidebarListItem isHeading={true}>Menu</SidebarListItem>
<SidebarListItem>
<Link to="/">Explore</Link>
</SidebarListItem>
<SidebarListItem>
<Link to="/blogs">Blogs</Link>
</SidebarListItem>
<SidebarListItem>
<Link to="/podcasts">Podcasts</Link>
</SidebarListItem>
<SidebarListItem>
<Link to="/youtube">Youtube</Link>
</SidebarListItem>
</SidebarMenu>
{auth.isAuthenticated ? SidebarCreateButton : SidebarAuthButtons}
</SidebarNav>
</SidebarContainer>
);
}
export default memo(Sidebar);
Move SidebarAuthButtons and SidebarCreateButton outside of the functional component render scope making them into React components (currently they are just jsx saved to a variable). This should fix the rerenders.

Why this problem happend in my react function?

I'm trying to get information from redux but this error happen and i dont know how could i fix it. That's my first time with react and react Hooks, sorry but i'm lost.
Thank you in advance.
React Hook "useSelector" is called in function "header" which is neither a React function component or a custom React Hook function
My code:
import React from 'react';
import { useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import Notifications from '../Notifications';
import logo from '~/assets/headerLogo.svg';
import { Container, Profile, Content } from './styles';
export default function header() {
const profile = useSelector(state => state.user.profile);
return (
<Container>
<Content>
<nav>
<img src={logo} alt="GoBarber" />
<Link to="/dashboard">DASHBOARD</Link>
</nav>
<aside>
<Notifications />
<Profile>
<div>
<strong>{profile.name}</strong>
<Link to="/profile">Meu Perfil</Link>
</div>
<img
src={
profile.avatar.url ||
'https://api.adorable.io/avatars/50/abott#adorable.png'
}
alt="profile"
/>
</Profile>
</aside>
</Content>
</Container>
);
}
The rules of hooks lint plugin depends on naming conventions to tell what is a component, what is a hook, and what is any other function. Functions beginning with use (eg, useEffect, useMyCustomStuff) are assumed to be hooks. Functions beginning with a capital letter are assumed to be components. Your code does neither, so it assumes this is just a normal function unrelated to hooks or components.
Rename header to Header to fix this.

react-router connected to redux : works with links but only the URL change when dispatching push

I'm trying to programmatically push an URL to navigate with react-router, redux, and connected-react-router
When clicking on a <Link /> button, it's working great, the URL is changing and the route too.
But when using a dispatch(push(url)), the URL only change and the content is not updated
I've made a minimal example here.
Any help would be really grateful,
Thanks
A lot of anti-pattern code, poor application structured, and mixing of packages is holding your application back.
I rewrote it entirely, here's what I've done:
Reconfigured your application folder's structure to be standard.
Don't mix Router (BrowserRouter) with ConnectedRouter.
Don't place all of your components within the App.js file.
Since the Header is always mounted, you don't need redux, instead you can just use withRouter (it exposes route props to the component).
Your rootReducer is missing a reducer, so I added a dummyReducer that just returns state.
Stick to Link or this.props.history when navigating. For this example, there's no need to use both. Also, you don't need to use ConnectedRouter's push function, because the history is passed as a prop when using withRouter.
Side note: If you want the Header to be a "router" where all route changes pass through here, then you'll need to create an action and a reducer that passes a string and stores it to the redux's store. The Header is then connected to the redux store and updates the route when this string has changed.
Working example: https://codesandbox.io/s/526p7kjqq4
components/Header.js
import React, { PureComponent, Fragment } from "react";
import { withRouter } from "react-router-dom";
class Header extends PureComponent {
goTo = route => {
this.props.history.push(route);
};
render = () => (
<Fragment>
<ul>
<li>
<button onClick={() => this.goTo("/")}> Announcements </button>
</li>
<li>
<button onClick={() => this.goTo("/shopping")}> Shopping </button>
</li>
</ul>
<div>
<button onClick={() => this.goTo("/shopping")}>
Click here to go shopping ! (if you can...)
</button>
</div>
</Fragment>
);
}
export default withRouter(Header);
routes/index.js
import React from "react";
import { Switch, Route } from "react-router-dom";
import Announcements from "../components/annoucements";
import Shopping from "../components/shopping";
export default () => (
<div style={{ padding: "150px" }}>
<Switch>
<Route exact path="/" component={Announcements} />
<Route path="/shopping" component={Shopping} />
</Switch>
</div>
);
components/App.js
import React, { Fragment } from "react";
import Routes from "../routes";
import Header from "./Header";
export default () => (
<Fragment>
<Header />
<Routes />
</Fragment>
);
Here is what you're trying to accomplish: https://codesandbox.io/s/8nmp95y8r2
However, I DO NOT recommend this as it's a bit unnecessary, when history is either already passed as a prop from the Route or can be exposed when using withRouter. According to the Redux docs, it's not recommended either. And instead to either use Link or pass the history prop to the redux action creator instead of programmatic navigation through redux state.
containers/Header.js
import React, { PureComponent, Fragment } from "react";
import { connect } from "react-redux";
import { push } from "connected-react-router";
class Header extends PureComponent {
goTo = route => this.props.push(route); // this is equivalent to this.props.dispatch(push(route)) -- I'm just appending dispatch to the push function in the connect function below
render = () => (
<Fragment>
<ul>
<li>
<button onClick={() => this.goTo("/")}> Announcements </button>
</li>
<li>
<button onClick={() => this.goTo("/shopping")}> Shopping </button>
</li>
</ul>
<div>
<button onClick={() => this.goTo("/shopping")}>
Click here to go shopping ! (if you can...)
</button>
</div>
</Fragment>
);
}
export default connect(
null,
{ push }
)(Header);
After reading the complete thread react-router-redux's push() not rendering new route, I came across this solution you need to use Router with passing down history as prop down to your app and don't use create from multiple files just import it from a common file.
Here is the working codesandbox: push rendering the new route

Resources