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>
);
}
Related
In the react hooks web, I have a component called NominatePerson, in that component if the props.role is not admin it should display text Nomination View and if its admin then it should display Dashboard. This is not happening, it always displays Dashboard. Could someone help me to resolve the issue ?
App.js
function App() {
const [role, setRole] = useState();
useEffect(() => {
const fetchData = async () => {
try {
const userEmail = localStorage.getItem("loginEmail");
const res = await Axios.get(
"http://localhost:8000/service/managenomineeaccess",
{ params: { userEmail } }
);
console.log(res.data[0][0].access, "rest.data");
const data = res.data[0][0].access;
setRole(data);
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
const switchAdmin = (
<Switch>
<Route exact path='/' component={() => <Login role={role} />} />
<ProtectedRoute exact path='/dashboard' component={DashboardLayout} />
<ProtectedRoute exact path='/manageNominees' component={ManageNominees} />
<Route path='/nominatePerson' exact component={NominatePerson} />
<Route path='/nominationView' exact component={NominationView} />
</Switch>
);
const switchUser = (
<Switch>
<Route exact path='/' component={Login} />
<Route
path='/nominatePerson'
exact
component={() => <NominatePerson role={role} />}
/>
<Route
path='/nominationView'
exact
component={() => <NominationView role={role} />}
/>
<Route component={NominationView} />
</Switch>
);
return (
<Router>
<div>
<ThemeProvider theme={theme}>
<GlobalStyles />
{role === "admin" ? switchAdmin : switchUser}
</ThemeProvider>
</div>
</Router>
);
}
export default App;
NominatePerson
const NominatePerson = (props) => {
return (
<div className='leftNavItem'>
<a>
<Link
to={props.role ? "/nominationView" : "/dashboard"}
className='nav-link'
>
<b>{props.role ? "Nomination View" : "Dashboard"}</b>
</Link>
</a>
</div>
)
}
server.js // get service
app.get("/service/managenomineeaccess", async (req, res) => {
try {
let userEmail = req.query.userEmail;
let data = await sequelize.query(
`SELECT access FROM devchoice.managenominees where email="${userEmail}";`
);
res.status(200).send(data);
} catch (e) {
res.status(500).json({ fail: e.message });
}
});
I'm updating a sourcecode from react-router-5 to version 6. So far I'm getting this error:
Error: [div] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
The bug is triggered when I activate a link in this component(QuoteItem.js):
const QuoteItem = (props) => {
return (
<li className={classes.item}>
<figure>
<blockquote>
<p>{props.text}</p>
</blockquote>
<figcaption>{props.author}</figcaption>
</figure>
<div className={classes.item}>
<Link className='btn' to={`/quotes/${props.id}`}>View Fullscreen</Link>
</div>
</li>
);
};
in another component (VerQuotes) I defined the routes:
const VerQuotes = () => {
return (
<div>
<main>
<Layout>
<Routes>
<Route path="quotes" element={<AllQuotes />} />
<Route path="new-quote" element={<NewQuote />} />
<Route path="quotes/:quoteId" element={<QuoteDetail />} />
</Routes>
</Layout>
</main>
</div>
);
};
I'm kind of lost how to tackle the error, your comments will be highly appreciated.
Thanks a lot
Update
QuoteDetail
const QuoteDetail = () => {
const match = useNavigate();
const params = useParams();
const { quoteId } = params;
const { sendRequest, status, data: loadedQuote, error } = useHttp(getSingleQuote, true);
//const quote = DUMMY_NOTES.find((quote) => quote.id === params.quoteId);
useEffect(() => {
sendRequest(quoteId);
}, [sendRequest, quoteId]);
if(status === "pending"){
return (
<div className="centered">
<LoadingSpinner />
</div>
);
}
if(error){
return <p className="centered">{error}</p>;
}
if (!loadedQuote.text) {
return <p>No Quote Found!</p>;
}
return (
<Fragment>
<HighlightedQuote text={loadedQuote.text} author={loadedQuote.author} />
<Routes>
<Route path={match}>
<div className="centered">
<Link
className="btn--flat"
to={`${match}/comments`}
>
Load Comments
</Link>
</div>
</Route>
<Route path={`${match}/comments`} element={<Comments />}></Route>
</Routes>
</Fragment>
);
};
Issues
After tracing your code I found you had a couple issues in QuoteDetail component.
You used const match = useNavigate(); (so match is really the navigate function) but then later used match to attempt to form a path string for a Route.
The Route component's children prop is only for rendering nested routes. The error you see is the use of the div element that isn't a Route component.
Code
<Routes>
<Route path={match}>
<div className="centered">
<Link
className="btn--flat"
to={`${match}/comments`}
>
Load Comments
</Link>
</div>
</Route>
<Route path={`${match}/comments`} element={<Comments />}></Route>
</Routes>
Solution
Remove const match = useNavigate(); since it is not used, and place the div into the element prop of the Route. Change the path props to use relative routing from the current route path that's already been built up.
const QuoteDetail = () => {
const params = useParams();
const { quoteId } = params;
const { sendRequest, status, data: loadedQuote, error } = useHttp(
getSingleQuote,
true
);
//const quote = DUMMY_NOTES.find((quote) => quote.id === params.quoteId);
useEffect(() => {
sendRequest(quoteId);
}, [sendRequest, quoteId]);
if (status === "pending") {
return (
<div className="centered">
<LoadingSpinner />
</div>
);
}
if (error) {
return <p className="centered">{error}</p>;
}
if (!loadedQuote.text) {
return <p>No Quote Found!</p>;
}
return (
<Fragment>
<HighlightedQuote text={loadedQuote.text} author={loadedQuote.author} />
<Routes>
<Route
path="/"
element={
<div className="centered">
<Link className="btn--flat" to="comments">
Load Comments
</Link>
</div>
}
/>
<Route path="comments" element={<Comments />} />
</Routes>
</Fragment>
);
};
I am using React Router v6 in an application. I have a layout page, which uses an outlet to then show the main content. I would also like to include a title section that changes based on which path has been matched, but I am unsure how to do this.
function MainContent() {
return (
<div>
<div>{TITLE SHOULD GO HERE}</div>
<div><Outlet /></div>
</div>
);
}
function MainApp() {
return (
<Router>
<Routes>
<Route path="/projects" element={<MainContent />} >
<Route index element={<ProjectList />} title="Projects" />
<Route path="create" element={<CreateProject />} title="Create Project" />
</Route>
<Routes/>
</Router>
);
}
Is something like this possible? Ideally, I would like to have a few other props besides title that I can control in this way, so a good organization system for changes like this would be great.
The most straightforward way would be to move the title prop to the MainContent layout wrapper and wrap each route individually, but you'll lose the nested routing.
An alternative could be to create a React context to hold a title state and use a wrapper component to set the title.
const TitleContext = createContext({
title: "",
setTitle: () => {}
});
const useTitle = () => useContext(TitleContext);
const TitleProvider = ({ children }) => {
const [title, setTitle] = useState("");
return (
<TitleContext.Provider value={{ title, setTitle }}>
{children}
</TitleContext.Provider>
);
};
Wrap the app (or any ancestor component higher than the Routes component) with the provider.
<TitleProvider>
<App />
</TitleProvider>
Update MainContent to access the useTitle hook to get the current title value and render it.
function MainContent() {
const { title } = useTitle();
return (
<div>
<h1>{title}</h1>
<div>
<Outlet />
</div>
</div>
);
}
The TitleWrapper component.
const TitleWrapper = ({ children, title }) => {
const { setTitle } = useTitle();
useEffect(() => {
setTitle(title);
}, [setTitle, title]);
return children;
};
And update the routed components to be wrapped in a TitleWrapper component, passing the title prop here.
<Route path="/projects" element={<MainContent />}>
<Route
index
element={
<TitleWrapper title="Projects">
<ProjectList />
</TitleWrapper>
}
/>
<Route
path="create"
element={
<TitleWrapper title="Create Project">
<CreateProject />
</TitleWrapper>
}
/>
</Route>
In this way, MainContent can be thought of as UI common to a set of routes whereas TitleWrapper (you can choose a more fitting name) can be thought of as UI specific to a route.
Update
I had forgotten about the Outlet component providing its own React Context. This becomes a little more trivial. Thanks #LIIT.
Example:
import { useOutletContext } from 'react-router-dom';
const useTitle = (title) => {
const { setTitle } = useOutletContext();
useEffect(() => {
setTitle(title);
}, [setTitle, title]);
};
...
function MainContent() {
const [title, setTitle] = useState("");
return (
<div>
<h1>{title}</h1>
<div>
<Outlet context={{ title, setTitle }} />
</div>
</div>
);
}
...
const CreateProject = ({ title }) => {
useTitle(title);
return ...;
};
...
<Router>
<Routes>
<Route path="/projects" element={<MainContent />}>
<Route index element={<ProjectList title="Projects" />} />
<Route
path="create"
element={<CreateProject title="Create Project" />}
/>
</Route>
</Routes>
</Router>
I was facing the same issue for a left-right layout: changing sidebar content and main content, without repeating styling, banner, etc.
The simplest approach I found was to remove nested routing, and create a layout component in which I feed the changing content through properties.
Layout component (stripped for this post):
export function Layout(props) {
return (
<>
<div class="left-sidebar">
<img id="logo" src={Logo} alt="My logo" />
{props.left}
</div>
<div className='right'>
<header className="App-header">
<h1>This is big text!</h1>
</header>
<nav>
<NavLink to="/a">A</NavLink>
|
<NavLink to="/b">B</NavLink>
</nav>
<main>
{props.right}
</main>
</div>
</>
);
}
Usage in react router:
<Route path="myPath" element={
<Layout left={<p>I'm left</p>}
right={<p>I'm right</p>} />
} />
Another solution is to use the handle prop on the route as described in the useMatches documentation.
import { useMatches } from "react-router-dom";
function MainContent() {
const matches = useMatches()
const [title] = matches
.filter((match) => Boolean(match.handle?.title))
.map((match) => match.handle.title);
return (
<div>
<div>{title}</div>
<div><Outlet /></div>
</div>
);
}
function MainApp() {
return (
<Router>
<Routes>
<Route path="/projects" element={<MainContent />} >
<Route index element={<ProjectList />} handle={{ title: "Projects" }} />
<Route path="create" element={<CreateProject />} handle={{ title: "Create Project" }} />
</Route>
<Routes/>
</Router>
);
}
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
Link component
return (
<div key={driver.id}>
<Link to={"/drivers/" + driver.id}>
<OurStaffList driver={driver} />
</Link>
</div>
App.js
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/archived-routes" component={ArchivedRoutes} />
<Route path="/find-routes" component={FindRoutes} />
<Route path="/signup" component={SignUp} />
<Route path="/signin" component={SignIn} />
<Route path="/drivers/:driver_id" component={DriverProfile} />
</Switch>
And now here in the component where i need the route id, im getting 'error not recognized params...'
Can someone give me an idea how i can get the route props into the component so i can render the correct driver profile ?
const DriverProfile = ({ driver, getDrivers }) => {
useEffect(() => {
getDrivers();
// eslint-disable-next-line
}, []);
console.log();
return (
<div className="col s12">
<ul className="with-header">
{driver.drivers &&
driver.drivers.map(driver => {
return <DriverProfileList driver={driver} key={driver.id} />;
})}
</ul>
</div>
);
};
DriverProfile.propTypes = {
driver: PropTypes.object.isRequired
};
const mapStateToProps = (state, ownProps) => {
let id = ownProps.match.params.driver_id;
console.log(id);
return {
driver: state.driver
};
};
Answered by user6136000. Thanks
https://reacttraining.com/react-router/core/api/Hooks/useparams
So, in your DriverProfile function component:
let { driver_id } = useParams();
You can access all routing parameters in component by wrapping it with https://reacttraining.com/react-router/core/api/withRouter