So I have my routes that render a component:
const Layout = () => {
return (
<InitLayout>
<Switch>
<Redirect exact from='/' to='/home/recent' />
<Route path="/home/:category" exact component={Home}></Route>
<Route path="/about" exact component={About}></Route>
<Route path="/help" exact component={Help}></Route>
<Route path="/users/:userId" exact component={UserProfile}></Route>
<Route path="/ask" exact component={AskQuestion}></Route>
<Route path="/review" exact component={ReviewPost}></Route>
<Route path="/posts/:postId/review" exact component={ReviewPost}></Route>
<Route path="/users/:userId" exact component={UserProfile}></Route>
<Redirect from='*' to='/home/recent' />
</Switch>
</InitLayout>
);
};
In any of these components I would theoretically set the following effect:
useEffect(() => {
if (!isSsr) {
fetchPosts();
}
setSsrState({ isSsr: false }); // ==> Here
}, []);
This code will basically set an SSR state, to prevent the client to re-request data from the server when it has already been preloaded from the server.
Now this state needs to be set from any of the components, but I don't want to duplicate this code in all components. Forgetting to put it will result in bugs, so that is definitely a bad idea.
Is there a straightforward way to define an effect once, and have it called in every of the route's component?
After speaking with Trace, the issue was able to be resolved by wrapping the layout component with an HOC, which sets the SSR state to false.
Interestingly enough, by wrapping the component inside an HOC, this sets off the child components first before the HOC component. That way, we are able to first check on the child component level if the SSR is set. After the child component useEffect is ran, the HOC useEffect is then ran, which sets the SSR flag back to false.
Related
I'm attempting to setup the following UI based on routes
/things/ - display the list of items (Component A)
/things/new - display the list of items, with a modal form overlay (Component A and Component B)
/things/:slug - display a specific item (Component C)
With this setup:
/things/ would display Component A
/things/new would display Component A AND Component B
/things/:slug would display ONLY Component C
This is all in a nested route - /things. Which means the path from useRouteMatch returns /things
I have tried several combos with Switch, and attempted to use matchPath and useRouteMatch to differentiate /new from /:slug but with no luck. Every attempt results in one of the paths not returning the correct component.
For example:
<Switch>
<Route path="/:slug(exclude "new")" exact component={ComponentC} />
<Route path="/" exact component={ComponentA} />
</Switch>
<Route path="/new" component={ComponentB} />
Another option I've tried with no luck is to use regex on the slug, to match the slug pattern. Let's says the slug pattern is xxx-xxx-xxx-###, then I tried:
<Route path="/:slug((\w*-)*(\d*))" component={ComponentC) />
but this doesn't match the routes for some reason.
I'm thinking I could use location.pathname but this seems like a hack.
Is it possible to match and render routes as described above with standard <Route path="[something]" /> components with this setup?
This is why I love Stack Overflow. Asking the question often leads you to answer it yourself.
I was browsing the React Router docs for info on the regex and discovered the Route path property can accept an array of paths. So I went with this:
const { path } = useRouteMatch()
<Route path={[path, `${path}/new`]} exact component={ThingsList} />
<Switch>
<Route path={`${path}/new`} render={() => <NewThing path={path} />} />
<Route path={`${path}/:slug`} render={() => <Thing path={path} />} />
</Switch>
I have this method render, within which I am creating a local variable category. I need to set the value of this variable inside the html in return statement using JSX.
I am using curly brackets to assign the value to the local variable, but this is not working for me. eg {category='business'}
This is what I am doing currently
export default class App extends Component {
render() {
let category = 'about';
return (
<Router>
<div>
<NavBar/>
<Switch>
<Route path="/about">
</Route>
<Route path="/business">
{category='business'}
</Route>
<Route path="/entertainment">
{category='entertainment'}
</Route>
<Route path="/general">
{category='general'}
</Route>
<Route path="/health">
{category='health'}
</Route>
<Route path="/science">
{category='science'}
</Route>
<Route path="/sports">
{category='sports'}
</Route>
</Switch>
<News pageSize={5} country="in" category={category}/>
</div>
</Router>
)
}
}
This one instead shows the category on screen.
Note : I know there are multiple ways in which I can achieve this but I wanted to know how to set local variable within my method
First of all, look into functional components.
A Route component expects a component, not logic. Handle the actual variable change in that component or elsewhere via react-router's useLocation hook.
What you're attempting to do is to change the state of the variable. Cue React's useState and useEffect hooks (the component needs to be a functional component for hooks to work).
First import the hook:
import { useState } from 'react';
Then, in your case:
const [category, setCategory] = useState('about') // initial value
and when you need to update it:
setCategory('science') // new value
I need to render component with another route but this component must not cover all page. For example, I clicked on some question from stackoverflow list, and than I will receive animate from right to left modal, and I need to change route also
React router (I am using V4)
export default (
<Switch>
<App>
<Route exact={true} path="/" component={App} />
<Route exact={true} path="/product/:id" component={Product}/>
</App>
</Switch>
);
My product container looks like
export default function productContainer(ChildComponent) {
class ProductContainer extends Component {
render = () => {
return <ChildComponent/>
}
}
return ProductContainer;
}
And my product component
class Product extends Component {
render = () => {
return ("")
}
}
export default productContainer(Product);
When I emulate situation, which I describe above, my page fully rerendred and I don't see my App component Page
Have any idea, how I can resolve this issue?
People have asked how to render a modal as a route before in react-router without re-rendering (can't find the discussion right now). Essentially with react-router this is not possible. Each route change causes a re-render. That said, you can do what you want by nesting your routes.
Each component can return routes, so by using composition you can choose where to render any route.
For instance,
export default (
<Switch>
<App>
<Route path="/" component={App} />
</App>
</Switch>
);
Inside <App /> -
render() {
return (
<Something>
<PageHeader />
<Switch>
<Route path="/product/:id" component={Product}/>
</Switch>
</Something>
);
}
So you can see, if you were to add your routes inside the App component, they can all share a common page layout.
Remember: Any component can return multiple routes or just a single one inside a Switch! Switch will only render the first route that matches.
Also Remember: Routes inside a switch must be a direct child, you can't have Switch -> App -> Route, it must always be a direct child like Switch -> Route
Use render property instead of component in Route.
<Route exact={true} path="/" render={() => (
<div>
<App />
<Route exact={true} path="/product/:id" component={Product}/>
</div>
)} />
I have the following basic structure of nested routes/components for a react app:
/users -> UsersList
/users/:id -> UserLayout
/users/:id/ -> UserProfile
/users/:id/settings -> UserSettings
/users/:id/blah -> YetAnotherComponent
What I'm trying to figure out, in the context of react router v4, is how to access the :id parameter in the UserSettings component. I can access it fine in the UserLayout component, but nowhere else further downstream.
My main router definition lies in a Home component, but all the user-specific routes contain the same header information so I want all the user-specific routes to be nested. My current structure has these nested routes defined in the UserLayout component. However, no matter what I do to the layout component's route definition, I cannot get any other route than the "index" route (UserProfile) to render. When trying to access UserSettings or any other route, my top level 404 route gets hit instead.
Here's the relevant JSX (snippets of the actual components' render functions):
Home
<main>
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/users" component={UsersList} />
<Route exact path="/users/:id" component={UserLayout} />
<Route exact path="/" component={Home} />
<Route component={NoMatch} />
</Switch>
</main>
UserLayout
<div>
<Switch>
<Route path={`${match.url}/settings`} component={UserSettings} />
<Route path="/users/:id/blah" component={YetAnotherComponent} />
</Switch>
<Route path="/users/:id" component={UserProfile} />
</div>
In the UserLayout component I've tried both path formats shown in the Switch and have tried turning on/off exact matching. The only thing I can come up with is using the Route component's render parameter to pass the id parameter, but that just seems wrong. Is that my only option?
Of course only a few minutes after I post, I figured it out. In my top-level route config, I had set the /user/:id to require an exact match. This meant that when I navigated to /user/:id/anything_else react router didn't load UserLayout at all and thus didn't get the chance to test the remainder of the routes I had configured.
So I changed this from my home component:
<Route exact path="/users/:id" component={UserLayout} />
...to this:
<Route path="/users/:id" component={UserLayout} />
... and now all is well with the world.
Consider the following React Router setup:
<Router history={history}>
<Route path="/" component={Root}>
<Route path="/users" component={UserList}/>
<Route path="/users/new" component={UserCreate}/>
<Route path="/users/:id" component={User}/>
<Route path="/tags" component={TagList}/>
<Route path="/tags/new" component={TagCreate}/>
<Route path="/tags/:id" component={Tag}/>
</Route>
</Router>
At any point, I want to be able to display a specific modal. Right now, I am managing this with a modal property in my Redux store. The Root component (<Route path="/" component={Root}>) checks for this property and renders the necessary modal if it is set.
Can this be achieved via React Router? So rather than having to keep a modal property in my redux store, I can just navigate to something like /users#modal or /users/modal and get both the UserList component rendered (as defined in <Route path="/users" component={UserList}/>) and the modal component
One of the ways you can do this would be to check for your location hash in your componentDidMount of your individual components.
componentDidMount() {
if (window.location.hash) {
const hash = window.location.hash.substring(1);
if (hash === "modal") {
//show your modal here.
}
}
}
You could have all your components inherit from a common base class if they all need to behave this way.