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
Related
I am new to React and I have seen two different syntaxes for routing using react-router-dom, as illustrated below with Example1 and Example2. Welcome is just a standard react component.
Example1:
<BrowserRouter>
<Switch>
<Route exact path="/"><Welcome /></Route>
</Switch>
</BrowserRouter>
Example2:
<BrowserRouter>
<Switch>
<Route exact path="/" component={Welcome} />
</Switch>
</BrowserRouter>
Is there any difference between the two syntaxes? As of 2021 is there a preference?
The documentation already answers this for you:
When you use component (instead of render or children) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use the render or the children prop.
I'm attempting to pass a user's auth state down to components via a react-router-dom switch block (I believe I ought to look at implementing redux but that's a question for later).
There's a Home view that gets passed all the login information after the user authenticates, and I can see the authState object as a prop in the home component using react devtools:
import React from "react";
import Dashboard from "../Dashboard";
import {Switch, Route} from "react-router-dom";
import NoMatch from "../NoMatch";
function Home(props) {
// authState exists
return (
<Switch {...props}>
// authState exists
<Route exact path="/" render={(...props) => <Dashboard {...props} />} /> // authState gone
<Route component={NoMatch} />
</Switch>
);
}
export default Home;
after executing this it renders the Dashboard, and tracing down the component chain with react devtools I can see that the prop has been passed from the Home component to the Switch component successfully. However, once it gets to the Route the prop is gone, and the only props available in the Dashboard component are the history, location and match props.
Why are the props missing and what's the right way to pass them down?
Couple of improvements needed in your code:
Passing props to Switch component is unnecessary
No need to collect router props using the rest syntax only to spread them later
Main Problem:
props inside the function passed to render prop refers to the router props instead of the props passed to Home component. You are using the same identifier for two different things.
Solution
Use different names for router props and the props passed to Home component that you want to pass down to Dashboard component
function Home(props) {
return (
<Switch>
<Route
exact
path="/"
render={(routerProps) => <Dashboard {...routerProps} {...props} />}
/>
<Route component={NoMatch} />
</Switch>
);
}
Alternatively, if you don't need access to router props in the Dashboard component, remove the props parameter from the render prop function.
<Route
exact
path="/"
render={() => <Dashboard {...props} />}
/>
Now, you won't have access to router props inside the Dashboard component but the auth state of the user will be passed down to Dashboard component.
In the most recent versions of the react-router-dom you must replace render and component attributes with element. You cannot pass a callback function there in which there were specified the route props anymore.
Instead you can use a hook in your routed component:
const params = useParams();
to obtain the parameters.
See more in the official documentation.
I have my App component like this:
const App = (props) => (
<BrowserRouter>
<PageTheme {...some props I'd like to change on route change}>
<Switch>
<Route exact path="/example">
<Example {...props} />
</Route>
<Route exact path="/example/detail/:exampleId">
<ExampleDetail {...props} />
</Route>
</Switch>
</PageTheme>
</BrowserRouter>
);
The PageTheme component wrapping the Switch has some navigation UI, as well as some options about a page's background color, etc.. Is there a way to provide PageTheme some specific props for each route? Or is the best option to put a new PageTheme component inside of each Route? Thanks!
What you can do is to use withRouter and wrap PageTheme with it:
export default withRouter(PageTheme);
Or use useLocation hook if PageTheme is function component. Than you will have access to location and do your magic based on current route/url.
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.
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>
)} />