React Router Redirect to component not redirecting - javascript

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

Related

React - <Link> tag displays the dynamic url in browser but does not render the page unless the browser is refreshed

I am a newbie trying to build a React blog. Routes etc work fine except when a dynamic route comes into question. The Link does not render unless refreshed.
Here is App.js where the Routes are defined
function App() {
const [username, setUsername] = useState('');
const [loggedIn, setLoggedIn] = useState(false);
return (
<>
<Router history={customHistory}>
<div className="App">
<UserContext.Provider value={{ username, setUsername, loggedIn, setLoggedIn }}>
<Navigation />
<Switch>
<Route path="/login" component={Login} />
<Route path="/register" component={Register} />
<Route exact path="/blog/:id" component={BlogDetails} />
<Route path="/content" component={Content} />
<Route path="/logout" component={Logout} />
<Route exact path="/" component={Home} />
</Switch>
</UserContext.Provider>
</div>
</Router>
</>
)
}
export default App;
Here is BlogItems.js which calls the :
import { Link } from 'react-router-dom';
function BlogItems() {
const [blogList, setBlogList] = useState([])
useEffect(() => {
Axios.get("http://localhost:3001/api/get"
, { withCredentials: true })
.then((res) => {
setBlogList(res.data);
});
}, []);
return (
<div className="blog-list">
{blogList && blogList.map((blog) => {
return (
<div className="blog-elems" key={blog && blog.id}>
<Link to={`/blog/${blog.id}`}>{blog.title}</Link>
<h6>By <span>Ritu Rawat</span> on {DateFormater(blog.dated)}</h6>
<div className="blog-list-body">{blog && blog.body.slice(0, 200) + '...'}</div>
</div>
);
})}
</div>
) }
export default BlogItems;
and this is the actual BLogDetails Page which needs to be called from the dynamic route:
const BlogDetails = (props) => {
console.log(props);
//const { username } = useContext(UserContext);
const [blog, setBlog] = useState(null);
const { id } = useParams();
useEffect(() => {
console.log("BLOG ARTICLE");
Axios.get(`http://localhost:3001/api/blog/${id}`
, { withCredentials: true })
.then((res) => {
setBlog(res.data[0]);
});
}, [id]);
return (
<>
<div className="blog-background">
<div className="blog-list">
<div className="blog-elems" key={blog && blog.id}>
<h2> {blog && blog.title}</h2>
<h6>By <span>Ritu Rawat</span> on {blog && DateFormater(blog.dated)}</h6>
<div className="blog-list-body">{blog && blog.body}</div>
</div>
<Comments id={id} />
</div>
</div>
</>
)
}
export default BlogDetails;

ReactRouterDom, AuthRoute returns react render functions are not valid as react child warning

My Router is a simple component containing public and private routes. I have created an AuthRoute referring to the great tutorial from here
So, my Router looks like:
<Router>
<div>
<Navigation />
<Route exact path={ROUTES.LANDING} component={Landing} />
<Route path={ROUTES.SIGN_UP} component={SignUp} />
<Route path={ROUTES.SIGN_UP_SUCCESS} component={SignUpSuccess} />
<AuthenticationRoute path={ROUTES.HOME} component={Home} />
</div>
</Router>
and my AuthenticationRoute looks like this:
export const AuthenticationRoute = ({ component: Component, ...rest }) => {
const [authChecking, setAuthChecking] = useState(true);
const [{ isAuth }, dispatch] = useStateValue();
useEffect(() => {
checkLoggedIn().then(res => {
setAuthChecking(false);
dispatch({
op: 'auth',
type: 'toggleSessionAuth',
toggleSessionAuth: res
});
});
}, [])
if(authChecking)
return null;
if(!isAuth) {
return <Redirect to='/' />;
}
return <Route {...rest} render={(props) => (
<Component {...props} />
)
} />
}
Everything looks fine, however, my console returns such warning:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from the render. Or maybe you meant to call this function rather than return it.
I have tried different solutions using component/render etc, however, I could not find a solution to this problem and I have no idea what I am doing wrong.
For testing purposes, instead of rendering Component, I tried to render simple <div>test</div> and it worked fine. However, when I am passing a JSX component in props, it returns the warning shown above.
Implementation oh Home Component (Home.js):
export const Home = () => {
const [{ user }, dispatch] = useStateValue();
const { history } = useReactRouter();
const moveTo = path => {
dispatch({
op: 'other',
type: 'setView',
setView: path
});
history.push(path);
}
return (
<div className="pageMenuWrapper">
<h1 className="homeTitle">Hi {() => user ? `, ${user.username}` : ``}.</h1>
<div className="wrapper">
<Tile image={leagueico} alt="text" onClick={() => moveTo(ROUTES.TEST)}/>
<Tile comingSoon />
</div>
</div>
);
}
export default Home;
Could anyone help me solve this little problem?

How to use ternary operator to return jsx using react?

I want to hide a component if the user is in the "/items" page.
below is my code,
function Main() {
const isAdmin = getUser();
return(
<Switch>
<Route
exact
path="/items"
render={routeProps => (
<Layout>
{isAdmin ? <Items {...routeProps} />: <NotFound/>}
</Layout>
)}
/>
//other Routes
</Switch>
);
}
const Layout: React.FC = ({ children }) => (
<>
<TopBar />
{children}
<BottomBar />
</>
);
Now when the user is in /items page I don't want the TopBar and BottomBar to be displayed.
how can I do it? could someone help me with this? thanks.
Change your Layout component as below:
const Layout: React.FC = ({ children }) => {
const history = useHistory();
const isItemsPath = history.location.pathname.includes("/items");
return (
<>
{!isItemsPath && <TopBar />}
{children}
{!isItemsPath && <BottomBar />}
</>
);
}

React Router renders the target page on the same page

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>

the logout component not rendering after the authenticated is turned to true this is similar code as from react-router docs

I just tried to build the react-router docs ex on browser but there is problem in AuthButton component it isn't showing signOut button when the isAuthenticated turns true
import React from 'react';
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect,
useHistory,
useLocation,
} from 'react-router-dom';
export default function AuthExample() {
return (
<Router>
<div>
<AuthButton />
<ul>
<li>
<Link to='/public'>Public Page</Link>
</li>
<li>
<Link to='/protected'>Protected Page</Link>
</li>
</ul>
<Switch>
<Route path='/public'>
<PublicPage />
</Route>
<Route path='/login'>
<LoginPage />
</Route>
<PrivateRoute path='/protected'>
<ProtectedPage />
</PrivateRoute>
</Switch>
</div>
</Router>
);
}
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
fakeAuth.isAuthenticated = true;
setTimeout(cb, 100); // fake async
},
signout(cb) {
fakeAuth.isAuthenticated = false;
setTimeout(cb, 100);
},
};
function AuthButton() {
let history = useHistory();
return fakeAuth.isAuthenticated ? (
<p>
Welcome!{' '}
<button
onClick={() => {
fakeAuth.signout(() => history.push('/'));
}}>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
);
}
function PrivateRoute({ children, ...rest }) {
return (
<Route
{...rest}
render={({ location }) =>
fakeAuth.isAuthenticated ? (
children
) : (
<Redirect
to={{
pathname: '/login',
state: { from: location },
}}
/>
)
}
/>
);
}
function PublicPage() {
return <h3>Public</h3>;
}
function ProtectedPage() {
return <h3>Protected</h3>;
}
function LoginPage() {
let history = useHistory();
let location = useLocation();
let { from } = location.state || { from: { pathname: '/' } };
let login = () => {
fakeAuth.authenticate(() => {
history.replace(from);
});
};
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={login}>Log in</button>
</div>
);
}
The reason it's not updating is because it doesn't know to update. You change the route but AuthButton doesn't know to re-render based on the route you need to pass it a prop so that it knows when to update. I refactored your code to incorporate using react hooks. By using hooks you can store isAuthenticated in local state in AuthExample via useState.
From AuthExample, pass down the state value for isAuthenticated as a prop to AuthButton. If the prop changes, AuthButton will detect it and this will trigger a re-render of AuthButton and reflect the correct component structure you are looking for. See below.
import React, { useState } from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect,
useHistory,
useLocation
} from "react-router-dom";
export default function AuthExample() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const fakeAuth = {
isAuthenticated: isAuthenticated,
authenticate(cb) {
fakeAuth.isAuthenticated = true;
setIsAuthenticated(true);
setTimeout(cb, 100); // fake async
},
signout(cb) {
setIsAuthenticated(false);
fakeAuth.isAuthenticated = false;
setTimeout(cb, 100);
}
};
return (
<Router>
<div>
<AuthButton fakeAuth={fakeAuth} isAuthenticated={isAuthenticated} />
<ul>
<li>
<Link to="/public">Public Page</Link>
</li>
<li>
<Link to="/protected">Protected Page</Link>
</li>
</ul>
<Switch>
<Route path="/public">
<PublicPage />
</Route>
<Route path="/login">
<LoginPage fakeAuth={fakeAuth} />
</Route>
<PrivateRoute path="/protected" fakeAuth={fakeAuth}>
<ProtectedPage />
</PrivateRoute>
</Switch>
</div>
</Router>
);
}
function AuthButton(props) {
const { fakeAuth, isAuthenticated } = props;
let history = useHistory();
return isAuthenticated ? (
<p>
Welcome!{" "}
<button
onClick={() => {
fakeAuth.signout(() => history.push("/"));
}}
>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
);
}
function PrivateRoute({ children, ...rest }) {
const { fakeAuth } = rest;
return (
<Route
{...rest}
render={({ location }) =>
fakeAuth.isAuthenticated ? (
children
) : (
<Redirect
to={{
pathname: "/login",
state: { from: location }
}}
/>
)
}
/>
);
}
function PublicPage() {
return <h3>Public</h3>;
}
function ProtectedPage() {
return <h3>Protected</h3>;
}
function LoginPage(props) {
const { fakeAuth } = props;
let history = useHistory();
let location = useLocation();
let { from } = location.state || { from: { pathname: "/" } };
let login = () => {
fakeAuth.authenticate(() => {
history.replace(from);
});
};
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={login}>Log in</button>
</div>
);
}
You can also see a working example in this code sandbox. There are a few ways to do this but hooks make it easy to manipulate state values to update functional components without having to make them class components. This way also keeps most of your code intact as is just adding a few checks for when isAuthenticated is updated.
I think the problem is in rendering process.
In my opinion, if you put the sub-functions in to the exported function, this problem may solve.
If the problem won't solve, try the class base component for handling this rendering process.
wish you success

Categories

Resources