React Router renders the target page on the same page - javascript

export const Bar = (props) => {
const classes = useStyles();
return (
<Fragment>
<div className={classes.barWrapper}>
{
props.releaseData.map((item, idx) => {
return (
<Router>
<Link className={classes.link} to={`/version/${item.id}`}><BarComponent/></Link>
<Switch>
<Route path="/version/:id" children={<VersionPage />} />
</Switch>
</Router>
)
})
}
</div>
</Fragment>
);
}
I have a component that renders multiple bars, which is clickable and has links to a child component called VersionPage.
export const VersionPage = (props) => {
const classes = useStyles();
const { releaseTitle } = props
let { id } = useParams();
return (
<Fragment>
<h3}>{id}</h3>
</Fragment>
);
}
In the child component, I use useParams() to render the id passed in through the URL. However, the problem is that it does not redirect the user to the new page /release/:id, but it renders this VersionComponent on the same page.
How can I make it redirect to a new page with the id passed in?

This will open the page in new Tab
<Link target='_blank' className={classes.link} to={`/version/${item.id}`}><BarComponent/></Link>

Related

How to correctly pass a callback and a state to the Layout element in React Router Dom?

How to correctly pass callbacks and states to the Layout so that they can be used elsewhere? When I share this as below, I have errors and a white screen:
class Menu extends Component {
constructor(props) {
super(props);
this.onSearchF = this.onSearchF.bind(this)
}
state = {
searchBlock: false,
};
onSearchF = (keyword) => {
const filtered = this.state.data.filter((entry) =>
Object.values(entry).some(
(val) => typeof val === "string" && val.toLowerCase().includes(keyword.toLowerCase())
)
);
};
render() {
return (
<div className="content">
<Routes>
<Route path="/" element={<Layout searchBlock={this.state.searchBlock} onSearch={()=>this.onSearchF()}/>}>
<Route
index
element={
<Home data={this.state.data} num={this.state.data.length} />
}
/>
</Route>
</Routes>
</div>
);
}
}
export default Menu;
Here I pass the callback to the Header that I previously passed to the Layout:
const Layout = () => {
return (
<>
<Header sblock={this.props.searchBlock} onS = {this.props.onSearch}/>
</>
);
};
export default Layout;
I want to use the callback here:
function Header() {
return (
<header className="header">
<button onClick={()=>console.log(this.props.sblock)}>button</button>
</header>
);
}
export default Header;
Your Layout is a functional component, and you are trying to use this.props in it; this is incorrect. Get the props as part of arguments instead, like so:
import { Outlet } from "react-router-dom";
const Layout = ({searchBlock,onSearch}) => {
return (
<>
<Header sblock={searchBlock} onS={onSearch}/>
<Outlet/>
</>
);
};
export default Layout;
Issues
The Layout component isn't accepting any props.
The Layout component isn't rendering an Outlet for nested routes.
Solution
It seems that Layout only exists to render the Header component. I'd suggest rendering Header directly in the Main component.
Example:
class Menu extends Component {
state = {
data: [],
searchBlock: false,
};
onSearch = (keyword) => {
const filtered = this.state.data.filter((entry) =>
Object.values(entry).some((val) =>
typeof val === "string"
&& val.toLowerCase().includes(keyword.toLowerCase())
)
);
... do something with filtered ...
};
render() {
const { data, searchBlock } = this.state;
return (
<div className="content">
<Header sblock={searchBlock} onS={this.onSearch} />
<Routes>
<Route
path="/"
element={<Home data={data} num={data.length} />}
/>
</Routes>
</div>
);
}
}
export default Menu;

React context api lose auth data when react router dom push page

I have an context where i save the user data, and i have another component when verify the context user is null, if the context user is null my component should redirect the user to the login page, if not should render the component. My routers is inside my Authprovider, but still losing the user data when reload the router. I found another posts with the same issue, and the instruction is to keep the routers inside the useauthprovider, but doesn't work with my app.
My code
function App() {
let header = window.location.pathname === '/login' || '/cadastro' ? <Header /> : null;
let footer = window.location.pathname === '/login' || '/cadastro' ? <Footer /> : null;
return (
<UseAuthProvider> // My use AuthProvider
<Router>
<div className='app-container' >
<Switch>
<Cart>
<Header />
<NavbarMenu />
<div className='app-body'>
<UseCampanhaProvider>
<PublicRoute exact path='/' component={Home} />
<PrivateRoute exact path='/cupom/:campaignId' component={CupomScreen} />
<PrivateRoute exact path='/carrinho' component={CartScreen} />
</UseCampanhaProvider>
<PublicRoute exact path='/login' restricted={true} component={Login} />
<PublicRoute path='/cadastro' restricted={true} component={Cadastro} />
</div>
<AuthModal />
{footer}
</Cart>
</Switch>
</div>
</Router >
</UseAuthProvider>
);
}
export default App;
My component where i verify the user context
const PrivateRoute = ({ component: Component, ...rest }) => {
const { user } = useAuth();
return (
<Route {...rest} render={props => (
!user ?
<Redirect to='/login' />
:
<Component {...props} />
)} />
);
};
export default PrivateRoute;
My context where i load the user
const UseAuthProvider = ({ children }) => {
const [user, setUser] = useState();
const [open, setOpen] = useState(false)
useEffect(() => {
verifyUser(); //here i call the function when verify the localstorage
}, [])
const verifyUser = async () => {
let tokenHeader = authHeader();
if (tokenHeader) {
await Api.post('/cliente/index', {}, {
headers: {
...tokenHeader
}
}).then((response) => {
setUser(response.data.cliente)
})
}
}
const handleModal = () => {
setOpen((state) => !state)
}
const Logout = async () => {
localStorage.clear('acessToken-bolao')
setUser(null)
}
return (
<useAuthContext.Provider value={{ Auth, verifyUser, user, Register, Logout, open, handleModal }}>
{children}
</useAuthContext.Provider>
)
}
I tried to debug my application and when i redirect my user to another router, before the component render my user return undefined, and after my component is rendered the context load the user data.
It sounds like your entire application is unmounting and remounting.
In this case the state will be lost as it is not simply a re-render.
By what mechanism are you navigating to the new page?
If I remember React-Router correctly you need to use
If you try navigating the url itself with window.location or href then you are reloading the entire page (not using the router in the SPA)
If routed correctly I would expect that only data inside the Switch would be re-loaded.

How to prevent re-rendering of a children component which has no props?

I have the following parent component in React;
const Landing = () => {
const { setHash, fetchError, fetchingMenu, fetchingConfig, fetchingCart,
SelectedMenuSet, SelectedMerchant, Config } = useContext(DataContext);
const hash = getHash();
useEffect(() => {
if (hash) setHash(hash);
}, [hash, setHash])
if (!hash) return <Error ReturnCode={600} Language={getBrowserLanguage()} />
if (fetchingConfig || fetchingCart || fetchingMenu) return <Spinner />;
if (fetchError) return <Error ReturnCode={fetchError}
Language{getBrowserLanguage()}/>
if (SelectedMenuSet.Layout === MenuLayout.Tile) return <Redirect to={'/tile'} />;
else {
if (Config.ShowMerchantList && !SelectedMerchant) return <Wellcome />
return <Redirect to={`${hash}/carousel/0`} />;
}}
export default Landing;
And i have a Loading page;
const Spinner = () => {
const classes = useStyles();
console.log("rerender")
return (
<ThemeProvider theme={theme}>
<div className={classes.spinner}>
<img className={classes.logo} alt="Logo" src={logo} />
<br />
<LinearProgress className={classes.progress} />
</div>
</ThemeProvider>
);
}
export default memo(Spinner);
I have some states on my DataContext and depends to these states i render different pages in the Landing page. Loading page is dependent to these states. For example im fetching a menu and its config in the DataContext, and when i start fetching, i set fetchingMenu true. When these states change, my child component re-renders itself. I tried to solve this problem with React.memo but it did not work.
Is there any solution to solve this re-rendering except memo or useMemo? If there is not how can i solve this problem with memo?

React Router Redirect to component not redirecting

I need a simple redirect to a component but its not working not sure why. This is the code:
const HomePage = () => {
const [videos, setVideos] = useState([]);
const videoClicked = (video) => {
return <Redirect to='/video' />
}
if(videos === []){
return <div>Loading ...</div>
}
return (
<div>
{videos.map(video => (
<div onClick={() => videoClicked(video)}>
<VideoThumbnail video={video} />
</div>
))}
</div>
)
}
export default HomePage
I have a useEffect in my HomePage function that I didnt include in this snippet that gives videos values. It works and when I onClick the div it calls videoClicked but the redirect doesnt work.
This is my router:
const App = () => {
return (
<HashRouter>
<Switch>
<Route exact path="/video" component={VideoPage} />
<Route path="/" component={HomePage} />
</Switch>
</HashRouter>
)
}
Also when I get this working is it possible to redirect to component and pass props thru it instead of just passing a string in the to tag.
You can have a new state and redirect based on that:
const HomePage = () => {
const [videos, setVideos] = useState([]);
const [clicked, setClicked] = useState(false);
const videoClicked = (video) => {
setClicked(true);
// return <Redirect to='/video' />
}
if (videos === []) {
return <div>Loading ...</div>
}
return (
clicked ? <Redirect to={{
pathname: '/video',
state: { someData: 'test' }
}} /> : (
<div>
{videos.map(video => (
<div onClick={() => videoClicked(video)}>
<VideoThumbnail video={video} />
</div>
))}
</div>
)
)
}
export default HomePage
and you can use props.location.state.someData in the component you're redirected to.
you can consider using History HTML5 instead :) simple and straightforward

React hooks - state in useState() is not reset when route is changed

const Create = () => {
console.log('rerender !!')
const [parcelType, setParcelType] = useState('paper')
console.log('parcelType =', parcelType)
return (
<Container onClick={() => setParcelType('plastic')}>
<BookingList />
<Card title="Business">
<p>Header</p>
</Card>
</Container>
)
}
export default Create
I want to change parcelType state to 'plastic' when click on Container in Create component. and I want to reset parcelType state to 'paper' when route is change ( Create component re-render ). But when component re-render state is not set to paper
For more details: CreateComponent is re-render when route is change in BookingList component
const BookingList = props => {
const { id } = props.match.params
const containerStyle = useTranslateSpring('-100px', '0')
const itemList = items.map((item, idx) => {
const itemStyle = useTranslateSpring('-100px', '0', '0', 200 + 200 * idx)
const url = `/booking/${item.id}/create`
return (
<ItemContainer
onClick={() => props.history.push(url)}
style={itemStyle}
key={item.id}
isactive={id === item.id}
>
{item.id}
</ItemContainer>
)
})
return <Container style={containerStyle}>{itemList}</Container>
}
export default withRouter(BookingList)
Create Component is render in route by routeTemplate
const Routes = () => (
<Router basename={process.env.REACT_APP_BASE_URL}>
<>
<RouteTemplate
exact
path="/booking/:id/create"
component={Booking.create}
title="Booking"
/>
</>
</Router>
)
and RouteTemplate is render Component wrapped by PageTemplate component
const RouteTemplate = props => {
const {
component: Component,
title,
query,
isAuthenticated,
isLanding,
...rest
} = props
return (
<Route
{...rest}
render={matchProps =>
isAuthenticated ? (
<PageTemplate title={title} isLanding={isLanding}>
<Component {...matchProps} query={query} />
</PageTemplate>
) : (
<Redirect
to={{
pathname: '/',
state: { from: props.location },
}}
/>
)
}
/>
)
}
So I assume you want to reset component's state once route is changed.
This should happen wherever you use functional component + hooks or class-based component with explicit this.state. It's how React works under the hood.
You already have <Create> rendered at the page
Once route is changed <Route> tries to render <Create> element
React sees there is already existing <Create> element and tries to update that instead of re-creating(typically update is much more efficient than re-creating). That's why state is not reset - since it should not reset for updates.
There are different way to handle that.
If such a case happen outside react-router's <Route> I'd suggest use key prop to reset state. But for <Route> it would mean replacing more clear/straightforward <Route path="..." component={Create} /> with more verboose <Route path="..." render={({match}) => <Create match={match} key={match.params.id} />}
So instead let's apply useEffect hook to reset state once props.match.params.id is changed:
const Create = ({ match: {params: {id} } }) => {
useEffect(() => {
setParcelType('paper');
}, [id]);
That should be equal to class-based
state = {
typeOfWhatEver: 'paper'
};
componentDidUpdate(prevProps) {
if(prevProps.match.params.id !== this.props.match.params.id) {
this.setState({
typeOfWhatEver: 'paper'
});
}
}

Categories

Resources