Single page With React Router V6 - reactjs

Hello I am trying to make a react single page using the react router v 6, but I have the following error:
TypeError
Cannot read property 'location' of undefined
and I'm also not able to imagine the logic of how to do this basically I just want to change my ContentOne to ContentTwo
code:
export default function App() {
return (
<Router>
<route />
</Router>
);
}
Routes:
export default function MainRoutes() {
return (
<Routes>
<MainWrapper>
<Route path="/" element={<Header />} />
<Route path="/" element={<ContentOne />} />
<Route path="/contenttwo" element={<ContentTwo />} />
<Route path="/" element={<Footer />} />
</MainWrapper>
</Routes>
);
}
Header:
const Render = () => {
const history = useHistory();
return (
<Header>
<button onClick={() => history.push("/")}>ContentOne</button>
<button onClick={() => history.push("/contenttwo")}>ContentTwo</button>
</Header>
);
};
export default Render;
contentOne:
const Render = () => {
return <ContentOne />;
};
export default Render;
contentTwo:
const Render = () => {
return <ContentTwo />;
};
export default Render;
Footer:
const Render = () => {
return <Footer />;
};
export default Render;
example:
https://codesandbox.io/s/sad-paper-ps1vw

Related

From static pagination to dynamic

Need help with pagination. Right now my app can change page, but if I want send request like /character?page=4 it always throw me /character?page=1 this is not help, coz I use router. I have no idea how to resolve my problem
My app.js
function App() {
return (
<>
<Router>
<Header />
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/character' component={AllCharacters} />
<Route component={NotFound} />
</Switch>
<Footer />
</Router>
</>
);
}
CharacterList.jsx
// API Data
const url = "https://rickandmortyapi.com/api/character";
// Fetching Page
const fetchPage = (page) => {
// Init loading while page load
setLoading(true);
const query = `${url}?page=${page}`;
fetchData(query);
setLoading(false);
};
// Change pages
const { push } = useHistory();
const handleChanger = (event, page) => {
fetchPage(page);
push({
pathname: "/character",
search: `?page=${page}`,
});
};
<Pagination
count={info.pages}
showLastButton
showFirstButton
onChange={handleChanger}
/>
Dynamic Pagination : App.js
function App() {
return (
<>
<Router>
<Header />
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/character/page=:page' component={DynamicPagination} />
<Route component={NotFound} />
</Switch>
<Footer />
</Router>
</>
);
}
Dynamic pagination Demo : DynamicPaginaton.js
export default function DynamicPagination() {
const history = useHistory();
const [page, setPage] = React.useState(0);
const [pageCount, setPagcount] = useState(10);
const handlePageChange = (event, value) => {
history.push(`/character/page=${value}`)
setPage(value);
};
return (
<div >
<Typography>page:{page}</Typography>
<Pagination
count={pageCount}
page={page}
onChange={handlePageChange}
style={{ marginTop: 20 }}
/>
</div>
);
}

Render components with layout

I have a layout where I get the user data and then a few pages.
what´s the correct way to use user data from the layout in the child components?
this is my Main component
const Main = () => {
return (
<Router>
<Route path="/" exact strict component={Homepage} />
<Layout>
<Route path="/friends" exact strict component={Friends} />
<Route path="/family" exact strict component={Family} />
</Layout>
</Router>
)
}
this is the layout
const Layout (props) => {
const [user, setUser] = useState({})
useEffect(() => {
getUser()
.then(setUser)
.catch(console.log('redirect to login page'))
}, [])
return(
<div>
<header>welcome {user.name}</header>
<div>{props.children}</div>
<footer>
<NavLink to="/family">Family</Navlink>
<NavLink to="/friends">Friends</Navlink>
</footer>
)
}
const Family = () => {return (<p>family</p>)}
const Friends = () => {return (<p>friends</p>)}
I´m not sure how to change the Main component to route those 2 urls to the components but using the Layout.
also this is rendering the layout evenn if I´m in the homepage
I´m wrapping Friends and Family in the Layout but it should be the other way around? how to avoid the render of the layout if im not in one of the pages that should use it?
Use render prop, pass in the user value from child(Layout) and call the children in Main component.
Also use a Switch component to avoid rendering the Layout component when you are on HomePage
Main Component
const Main = () => {
return (
<>
<Switch>
<Route path="/" exact strict component={Homepage} />
<Layout>
{(user) => <>
<Route path="/friends" exact strict render={(props) => <Friends {...props} user={user} />} />
<Route path="/family" exact strict render={(props) => <Family {...props} user={user} />} />
</>}
</Layout>
</Switch>
</>
)
}
Layout Component
const Layout = (props) => {
const [user, setUser] = useState({})
useEffect(() => {
getUser()
.then(setUser)
.catch(console.log('redirect to login page'))
}, []);
return(
<div>
<header>welcome {user.name}</header>
<div>{props.children(user)}</div>
<footer>
<NavLink to="/family">Family</Navlink>
<NavLink to="/friends">Friends</Navlink>
</footer>
</div>)
}
Issue: Rendering the layout even if I´m in the homepage
Solution: This is because Layout component is not a part of route. You need to use Switch
Issue Passing user prop to Family and Friends from Layout component
Solution
You can render Friends and Family as renderProp to route by defining the routes inside Layout component and passing user as prop.
const Layout = props => {
const [user, setUser] = useState({});
useEffect(() => {
getUser()
.then(setUser)
.catch(console.log('redirect to login page'))
}, []);
return (
<div>
<header>welcome {user.name}</header>
<Route path="/friends" exact strict render={(props) => <Friends {...props} user={user}/>} />
<Route path="/family" exact strict render={(props) => <Family {...props} user={user}/>} />
<footer>
<NavLink to="/family">Family</NavLink>
<NavLink to="/friends">Friends</NavLink>
</footer>
</div>
);
};
const Main = () => {
return (
<Router>
<Switch>
<Route path="/" exact strict component={Homepage} />
<Route component={Layout} />
</Switch>
</Router>
);
};
Solution 2:
Make use of context. In order to use context, ContextProvider needs to be in the hierarchy so route components need to be rendered directly inside the Layout component. In such case this can be used as below. Context is especially useful when your components have children and you need to use user down in the hierarchy
const UserContext = React.createContext(null);
const Friends = () => {
const user = React.useContext(UserContext);
return (
<div>
Friends<div>{user.name}</div>
</div>
);
};
const Family = props => {
const user = React.useContext(UserContext);
return (
<div>
Family<div>{user.name}</div>
</div>
);
};
const Layout = props => {
const [user, setUser] = useState({});
useEffect(() => {
// getUser()
// .then(setUser)
// .catch(console.log('redirect to login page'))
setUser({ name: "test name" });
}, []);
return (
<UserContext.Provider value={user}>
<div>
<header>welcome {user.name}</header>
<Route path="/friends" exact strict component={Friends} />
<Route path="/family" exact strict component={Family} />
<footer>
<NavLink to="/family">Family</NavLink>
<NavLink to="/friends">Friends</NavLink>
</footer>
</div>
</UserContext.Provider>
);
};

How to use React HOC for authenticated routing?

I'm trying to add private routing to my app, but I get this error:
"Invariant failed: You should not use <Route> outside a <Router>"
Here is my code:
App.js
class App extends Component {
render() {
return (
<BrowserRouter>
<Route render={({ history }) => (
<div className="App">
<Navbar history={history} />
<Switch>
<Auth path="/" component={HomePage} currUser={this.props.currUser} />
<Route path="/login" render={(props) => (<LoginPage {...props} login={this.props.login} />)} />
</Switch>
</div>
)} />
</BrowserRouter>
);
}
}
const Auth = ({ component: Component, ...rest }) => {
const {currUser} = rest;
return (
<Route {...rest} render=
{props =>
currUser ?
(<Component {...props} currUser={currUser.name} />) :
(<Redirect to={{ pathname: "/login", state: currUser }} />)
} />
)
}
const mapStateToProps = (state) => {
return {
currUser: state.auth.currUser
}
}
const mapDispatchToProps = {
...authActions,
}
export default connect(mapStateToProps, mapDispatchToProps)(Auth);
What am i doing wrong?
And how do I pass props from Redux state to the components in this method?
Thanks!
You wrapped the Auth component with the App component, so your file should export the App. When you export only the Auth, the <Route> tag in Auth is outside the router tag.
export default connect(mapStateToProps, mapDispatchToProps)(App);

React Router/WithRouter not redirecting even with WithRoute

I have a simple enough react app. I have two buttons/actions that redirect. sign out and add. signout is working but add is not.
before add click
history.location '/'
location '/'
after add click
history.location '/add'
location '/add'
but related component doesnt render.
router.js
let appHistory = createHistory();
const appRouter = () => (
<Router history={appHistory}>
<Switch>
<Route path="/signin" component={SignInUp} exact={true} />
<Route path="/" component={Landing} />
<Route path="/add" component={CreateEvent} />
<Route path="/eventview" component={EventDetails} />
</Switch>
</Router>
)
Main component
import React, {Component} from 'react';
import RequireAuth from './RequireAuth';
import {startSignOut} from '../actions/auth';
import {fetchEvents} from '../actions/event';
import {connect} from 'react-redux';
import {withRouter} from 'react-router';
import EventItem from './EventItem';
import Header from './Header';
const EventDisplay = class EventDisplay extends Component {
componentDidMount = () => {
this.props.fetchEvents();
}
handleAddEvent = () => {
console.log(this.props);
this.props.history.push('/add');
}
handleSignOut = () => {
this.props.startSignOut();
}
render() {
return (
<div>
<Header signOut={this.handleSignOut}/>
{
this.props.events.map((event, ind) => {
return <EventItem key={ind} history={this.props.history} index={ind + 1} event={event}/>
})
}
<button onClick={() => this.handleAddEvent()}>+</button>
</div>
)
}
}
const mapDispatchToProps = (dispatch) => ({
startSignOut: () => startSignOut(dispatch),
fetchEvents: (userId) => dispatch(fetchEvents(userId))
});
const mapStateToProps = (state) => ({
events: state.events
})
const connectedWithRouter = withRouter(connect(mapStateToProps, mapDispatchToProps)(RequireAuth(EventDisplay)));
export default connectedWithRouter;
Header.js
const Header = (props) => {
return (
<div>
<h2>Eventio</h2>
<span>circle</span>
<span>user</span>
<button onClick={() => props.signOut()}>Sign out</button>
</div>
)
}
export default Header;
Your Route with path / will be used for any path that is not /signin. Give it an exact prop and it will only be used for path /.
const appRouter = () => (
<Router history={appHistory}>
<Switch>
<Route exact path="/" component={Landing} />
<Route path="/signin" component={SignInUp} />
<Route path="/add" component={CreateEvent} />
<Route path="/eventview" component={EventDetails} />
</Switch>
</Router>
)

Target specific Router while using Link?

I have an app that looks like this:
<BrowserRouter>
<Route path="/" component={Root} />
</BrowserRouter>
// Root.js
function Root() {
return (
<MemoryRouter>
<Switch>
<Route path="/" component={MemRoot} />
<Route path="/bar">
<Link to="/>To browser Root</Link>
</Route>
</MemoryRouter>
);
}
Right now when I click on Link, it's going to change the URL of MemoryRouter, but I'd actually like it to mutate the URL of BrowserRouter.
How can I tell Link to target the BrowserRouter?
Alright, you can do this. See the demo:
import React, { Component } from 'react';
import { render } from 'react-dom';
import {
BrowserRouter,
MemoryRouter,
Route,
Switch,
Link
} from 'react-router-dom';
const Root = props =>
<BrowserRouter>
<Route path="/" render={ () =>
<div>
<p>Home</p>
<button
onClick={ props.onOpen }>
Start Wizard
</button>
</div> } />
</BrowserRouter>;
const MemRoot = props =>
<div>
<p>Wizard Home</p>
<button onClick={ props.onClose }>
Finish Wizard
</button>
<p>
<Link to='/bar'>/bar</Link>
</p>
</div>;
const Wizard = ({ onClose }) =>
<MemoryRouter>
<Switch>
<Route path="/bar" render={ () =>
<div>bar <Link to='/'>/</Link></div> } />
<Route exact path="/" render={ () =>
<MemRoot onClose={ onClose }/> } />
</Switch>
</MemoryRouter>;
const App = () =>
<BranchRoute />
class BranchRoute extends Component {
state = { out: false }
handleClose = () => this.setState({ out: true })
handleOpen = () => this.setState({ out: false })
render () {
const { out } = this.state;
return (
<div>
{ out
? <Root onOpen={ this.handleOpen }/>
: <Wizard onClose={ this.handleClose } />
}
</div>
);
}
}
render(<App />, document.getElementById('root'));

Resources