I know the title may be vague, so I'll try to describe my problem as good as I can.
I am doing a WordPress theme and I need to link to both categories and tags pages. Their structure is /[categories|tags]/item. Now I need to make a route for those two items only. I tried doing it like this:
<Route path="/category/:slug" component={Archives} />
<Route path="/tag/:slug" component={Archives} />
But with this I have no distinction of wether this is a category or a tag. I dont wan't to go with <Route path="/:type/:slug" component={Archives} /> because this may get confusing when I'll need to use nested pages. I also don't want to change the url scheme.
You can use queries to determine what the type is:
<Link to={{ pathname: '/category/[slug]', query: { type: 'category' } }} activeClassName="active">Some Link</Link>
Then access the queries in your Archives view via:
this.props.location.query // you could set up some conditional logic based on type
UPDATE: Without changing the url scheme
...
<Route path="/category/:slug" component={Archives} />
<Route path="/tag/:slug" component={Archives} />
// Then in Archives component you could do
class Archive extends React.Component {
...
render() {
const path = this.props.location.pathname;
const category = path.indexOf('/category/') !== -1;
const tag = path.indexOf('/tag/') !== -1;
... // some logic based on tag and category
return {
...
}
}
}
Related
Following this guide, I created the following file in my project:
/pages/user/[id].js
class Post extends Component {
render() {
return (
<React.Fragment>
<Navbar />
<Content />
<Footer />
</React.Fragment>
);
}
}
export default Post;
But when I go to that URL, I get a 404.
What is the problem?
Assuming you're visiting (for example), http://localhost:3000/user/something (where something is your id), try also visiting http://localhost:3000/user/something/ (note the backslash). This is currently a known issue in Next with dynamic routing.
(This also assumes you don't have pages/user/something.js in your project as dynamic routes take a back seat to explicitly named routes.)
I got a requirement from my client that we need to have custom themes in our react application.
Where user can select one of the available themes of his choice which will change some css attribute like font-color, font-family etc.
Right now I apply css rules to my application using className attribute.
I tried searching some of the ways of implementing the same and found one way that is having alternate css
<link rel="stylesheet" href="main.css">
<link rel="stylesheet alternate" href="light.css" id="light" title="Light">
<link rel="stylesheet alternate" href="dark.css" id="dark" title="Dark">
<script>
function enableStylesheet (node) {
node.rel = 'stylesheet';
}
function disableStylesheet (node) {
node.rel = 'alternate stylesheet';
}
</script>
And toggle rel value based on user selection.
Can any one suggest me any other way of achieving the same.
Thanks in advance.
Your usage is the correct way of implementing. But what if you've numerous theme? Loading all the css files will hamper the website. So, I would suggest you to update the source instead of loading all css files and enabling/disabling them.
node.ref = 'dark.css';
This way, you're requiring the css file only when in use. Once, the file is being used, next time it's cached. So no need to worry about them later. Obviously, it might take some time and may impact on performance if the filesize is huge, at initial use. Even though the best thing is that you don't need to wait for all.
But wait!!!
You're using react. React provides us context api and I would obviously utilize this in such scenario. Here's an excerpted example:
import {ThemeContext, themes} from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';
class App extends React.Component {
constructor(props) {
super(props);
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === themes.dark
? themes.light
: themes.dark,
}));
};
// State also contains the updater function so it will
// be passed down into the context provider
this.state = {
theme: themes.light,
toggleTheme: this.toggleTheme,
};
}
render() {
// The entire state is passed to the provider
return (
<ThemeContext.Provider value={this.state}>
<Content />
</ThemeContext.Provider>
);
}
}
function Content() {
return (
<div>
<ThemeTogglerButton />
</div>
);
}
ReactDOM.render(<App />, document.root);
Similar concept can be implemented using redux and with logger information to make our work easy.
I'm currently working on a administration for a site, and I was wondering what's the best way to check if user is logged in and render the routes to give access to it.
From meteor's documentation I can see: https://docs.meteor.com/api/accounts.html#Meteor-userId
Which is a quite a convenient way, but I'm wondering if it is secure enough to render administration routes on the client side.
Here's an example to what I would like to do :
const checkFunction=()=>{
if(Meteor.isClient){
return Meteor.userId();
}
}
const PrivateRoute = ({ layout, component, ...rest }) => (
<Route
{...rest}
render={props =>
checkFunction() ? (
React.createElement( layout, props, React.createElement(component, props))
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
Eventhough I make sure that every modifications on the database are also checked server side, I don't want to give an insecure access to my administration.
So, I have a relatively complex setup involving react-router and react-async-connect to render stuff server-side.
Here's my route setup:
<Route path='/' component={App}>
<Route path='profile/:userID' component={UserProfile} />
<Route path='*' component={NotFound} status={404} />
</Route>
So, when someone hits a profile/{userID} path, obviously the UserProfile component is rendered, which in turn makes a call to an external API to get props. However, if this API returns a 404, I want to display my 404 page.
The trouble is that it's a 404 from an external service, not a "real" router 404. How does one trigger this?
I really want to avoid importing the NotFound component into UserProfile component and doing it that way, seems quite dirty.
You can achieve this by using browserHistory function:
// UserProfile.js
import { browserHistory } from 'react-router';
// In place where you need redirect to 404:
browserHistory.push('/404-page')
I think, you can redirect to any route that don't matches your defined routes, as you are redirecting to NotFound page at every not matched route.
Keep your 404 application status in store/reducer as default false.
Upon getting 404 status code - set it to true.
Once user is clicked other link - reset to false.
In your App.render() do smth like this:
render() {
return isNotFound ? <NotFound /> : {this.props.children};
}
I've just found this library, react-server-status, that provides a friendly and easier way to provide the status page.
import React, { Component } from 'react';
import ServerStatus from 'react-server-status';
export default class NotFound extends Component {
render() {
return (
<ServerStatus status={ 404 }>
<div>Some content</div>
</ServerStatus>
);
}
}
I'm have a Redux app that uses react router with nested routes, like this:
<Provider store={store}>
<Router history={browserHistory}>
<Route name="sales" path="/" component={App}>
<IndexRoute name="home" component={Home} />
<Route name="reports" path="/reports" component={Reports}>
<IndexRoute name="reports-home" component={ReportsHome} />
<Route name="report-1" path="/reports/report-1" component={Report1}/>
<Route name="report-2" path="/reports/report-2" component={Report2}/>
</Route>
</Route>
</Router>
</Provider>
I'm trying to write a breadcrumbs component; so would like to be able to deterime the current route.
I've configured the component to receive the router using the withRouter function provided by react router:
Breadcrumbs = withRouter(Breadcrumbs);
This gives me a router object that looks like this:
Object {__v2_compatible__: true}
__v2_compatible__: true
createHref: createHref(location, query)
createKey: createKey()createLocation: createLocation(location)
createPath: createPath(location, query)
go: go(n)
goBack: goBack()
goForward: goForward()
isActive: isActive(location)
listen: listen(listener)
listenBefore: listenBefore(hook)
push: push(location)
pushState: ()
registerTransitionHook: registerTransitionHook(hook)
replace: replace(location)
replaceState: ()
setRouteLeaveHook: listenBeforeLeavingRoute(route, hook)
setState: ()
transitionTo: transitionTo(nextLocation)
unregisterTransitionHook: unregisterTransitionHook(hook)
__proto__: Object
Can I use this to determine the current route? Is there better way?
Getting location etc. via withRouter was added in react-router version 3.0. Dan Abramov recommends upgrading to 3.0 to use withRouter. From 2.7 to 3.0, it only provided the functions you saw.
Source: https://github.com/ReactTraining/react-router/blob/master/CHANGES.md#v300-alpha1
There is already a module that does this for you, I believe it's called react-router-breadcrumbs. I haven't tried it though.
If you want a custom solution, here's what you could do:
Use the this.props.routes and this.props.params objects. You can then map through the routes and for each entry make a lookup for such key in the params object. You can then create a string with said parameters.
Note that I have given each route (except IndexRoutes) a path attribute, because sometimes I want to display a custom name for a given page. For example:
<Route path="/:productId" name="Start" title="Start" component={StartView} />
Here's the solution on my app:
componentDidMount = () => {
this._prepareBreadCrumbs(this.props);
}
componentWillReceiveProps = (newProps) => {
this._prepareBreadCrumbs(newProps);
}
_prepareBreadCrumbs = (props) => {
let {routes, params} = props;
let breadcrumbPath = "";
let temp = routes.map(
(item, i) => {
if(item.path == null) return null; //if we are visiting an item without a path, ignore it.
if(i < routes.length-1 && routes[i+1].path != null) {
let arr = item.path.split(/[:/]|(:\/)/g); //sometimes the path is like "/:product_id/details" so I need to extract the interesting part here.
arr = arr.map(function(obj) {
return (obj in params) ? params[obj] : obj; //We now have ":product_id" and "details" - the first one will exist in the "params" object.
});
breadcrumbPath += arr.filter(Boolean).join("/") + "/"; //clean out some garbage and add the "/" between paths.
if(i == 0) return <li key={i}><Link to={breadcrumbPath}>YourSite.com</Link></li> //If on the root - display the site name
return <li key={i}><Link to={breadcrumbPath}>{item.name}</Link></li>
} else {
document.title = "YourSite.com - " + item.title; //set the document title if you want
if(i == 0) return <li key={i} className="active"><span>YourSite.com</span></li>
return <li key={i} className="active"><span>{item.name}</span></li>
}
}
);
this.setState({breadcrumbPath: temp});
}
render() {
<p>{this.state.breadCrumbPath || ""}</p>
}
You'd want to put this in your top-level React Component.