I'm quite new to reactjs and was just wondering if there is any easy way to display information from the same component to different routes. In the following code as an example I have just two functions that are just returning divs full of text, and calling them and rendering them right away (in the class or in the router) would just have them be on the same "page".
I've tried passing the ref by props but they always ended up undefined. I figured a state change would be awkward since there is no real "event". I'm using create-react-app, react-routerv4, and react-bootstrap.
In App.js
import React, { Component } from 'react';
import './App.css';
import NavBar from './Components/NavBar/NavBar.js';
import Band from './Components/Text/Band.js';
import { Router, BrowserRouter, Link, Route } from 'react-router-dom';
class App extends Component {
render() {
return(
<div>
<BrowserRouter>
<div className="RenderRouter">
<Route exact path='/' component={NavBar}/>
<Route exact path='/' component={ControlledCarousel}/>
<Route exact path='/' component={Home}/>
//<Route exact path='/Artists/ArtistX' component={Band}/>
<Route exact path='/Artists/Artist1' component={NavBar}/>
<Route exact path='/Artists/Artist1' render={props => <Band band1text = {this.props.band1text} />}/>
<Route exact path='/Artists/Artist2' component={NavBar}/>
<Route exact path='/Artists/Artist2' render={props => <Band band2text = {this.props.band2text} />}/>
</div>
</BrowserRouter>
</div>
);
}
}
export default App;
In Band.js
import React, { Component } from 'react';
import './Band.css';
class Band extends Component {
//Constructor for state change would go here
band1text(props) {
return(
<div id="band1Text" className="BandText">
<h1>"The best riffs!</h1>
</div>
);
};
band2text(props) {
return(
<div id="band2Text" className="BandText">
<p>More info coming soon! Check out the interview!</p>
</div>
);
};
//Create handlers to call functions, and pass reference?
render() {
return(
<div className="BandDescription">
//calling in DOM render object, can't pass props from here?
//{this.props.band1text()} = compiler error
{this.band1text()}
{this.band2text()}
</div>
);
}
}
export default Band;
It would probably be easier to just have separate components and classes for every piece of each route (i.e, BandX.js, CarouselX.js) but that could get verbose and one would have to import many files. I'm using react to build a music player component for the app as well, that's why I'm not just using standard JS.
Try writing something like this in your Band component render:
render() {
return(
<div className="BandDescription">
{this.props.band1text && this.band1text()}
{this.props.band2text && this.band2text()}
</div>
);
}
This way it checks for the prop before running whichever method. If both methods are passed, both functions will return. And you shouldn't need to pass props to those methods. Try writing them as arrow functions so they will be bound band1text = () => { ... }, you will still be able to access this.props.band1text from inside the method.
The props would be undefined because there is no props with bandText being passed down to App component. Routes are nested in App component and this.props.band1Text means you are expecting to read from props passed to App. Try passing band1Text and band2Text as props to App component.
Also to read a props that's not a function just use {this.props.band1Text} in the Band.js component
Related
I have a page that is displaying several of my star components that each have their own name and a prop called starType
I am generating several different of these stars with the following code
if (num > 0) {
return (
<div className="starWrapper">
<Star
name={`${makeid()}`}
starType={`${starList[Math.floor(Math.random() * 6 + 1)]} ${posList[Math.floor(Math.random() * 9 + 1)]}`}
></Star>
{makeStars((num - 1))}
</div>
);
And this is the star component
<NavLink to={props.name}>
<h1 className="star-label">{props.name}</h1>
<div className={``}>
<div className={`starBall ${props.starType}`}></div>
</div>
</NavLink>
At the moment I want the user to be able to click on each star and have it lead to a page. I have achieved that with react-router's dynamic routing
<Route
exact
path="/:id"
render={(props) => <GenerateSystem {...props} />}
/>
the issue is I want the page that is generated from my generateSystem component to have the starType prop passed to it by the star component. I am aware of React's one way data flow and I think that might be the issue. How can I pass prop data from an auto generated component to another auto generated component?
My full code is viewable here. The components I'm talking about are in the interstellar-view and systems folder.
since you are passing name through URL params so passing starType using query params is an easy option.
So URL would look like this www.example.com/123?starType=red-giant
In your star.jsx, make a modification like this
<NavLink to={`/${props.name}?starType=${props.starType}`}>
...
</NavLink>
In your App.js, make a modification like this
<Switch >
<Route exact path="/:id" component={GenerateSystem} />
<Route exact path="/sol" component={SolSystem} />
<Route exact path="/" component={Interstellar} />
</Switch>
(We do not need to render and pass props since we can use useParams in GenerateSystem.js)
In your GenerateSystem.js, make a modification like this
import React from "react";
import { Link, useLocation, useParams } from "react-router-dom";
function useQuery() {
return new URLSearchParams(useLocation().search);
}
export const GenerateSystem = (props) => {
const {name} = useParams();
const query = useQuery();
const starType = query.get('starType')
return(<div className={starType}>System <p>{name}</p></div>)
}
Refs:
https://reactrouter.com/web/api/Hooks/useparams
https://reactrouter.com/web/example/query-parameters
EDIT:
You can use Redux-store/Context-API to have a global store, so that name and starType can be stored globally and can be accessed in different components
More Use-cases Example -> for other people that came here:
As in React-Router-Dom V6-> there is no render method any more,
See Why does have an element prop instead of render or component?
We mentioned this in the migration guide from v5 to v6, but it's worth repeating here.
In React Router v6 we switched from using v5's and APIs to . Why is that?...
So I needed another way of dynamically rendering all routes for the Router, with a pre declared array with all routes:
const routingList = [{title: 'Home', search: '/', component: Home, icon: 'fa-home'},{...}]
<Routes>
{
routingList.map((routing) => {
let Child = routing.component;
return <Route key={routing.search} path={routing.search} element={<Child {...routing.compProps} />} />;
})
}
<Route path="*" element={<Notfound />} />
</Routes>
(BTW: if you also need the useLocation or the other hooks, and you are using React Class and not React functions, see my answer here:
Component with router props - For: Hooks can only be called inside of the body of a function component
)
I am using HOC(Layout component here) to wrap my custom component. The Layout component contains Header and sidebar. On clicking link, it will be rendering the respective component. But my problem is that with every route, my HOC gets rendered as route target component is wrapped in this HOC. How can I make my HOC render only once.
Example Snippet.
App.js
<Router>
<Switch>
<PrivateRoute path="routeOne" component={RouteOne}/>
<PrivateRoute path="routeTwo" component={RouteTwo}/>
</Switch>
</Router>
RouteOne.js
import React from "react"
import Layout from "/hoc"
const RouteOne = () =>{
return({..jsx..})
}
export default Layout(RouteOne)
Layout.js
const Layout(WrappedComponent) => {
const userDetails = useSelector(state);
useEffect(()=>{
dispatch(fetchSomething())
},[dispatch])
return ( <HeaderNavbarUILayout header={<Header
username={userDetails.userName}>
content={<WrappedComponent/>);
}
export default Layout
I want to render my HOC component only once. How can I do that?
EDIT:
I would follow this pattern https://simonsmith.io/reusing-layouts-in-react-router-4
const DefaultLayout = ({component: Component, ...rest}) => {
return (
<Route {...rest} render={matchProps => (
<div className="DefaultLayout">
<div className="Header">Header</div>
<Component {...matchProps} />
<div className="Footer">Footer</div>
</div>
)} />
)
};
then where you would normally define routes, replace it with this:
<DefaultLayout path="/" component={SomeComponent} />
I would take a look at the following docs:
how to use useEffect
https://reactjs.org/docs/hooks-reference.html#useeffect
how to implement should component update
https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-shouldcomponentupdate
conditionally firing an effect
```
useEffect(
() => {
...
};
},
[whatever you're watching],
);
```
The hoc being wrapped around is rendered every time and that is expected. But the React's diffing algorithm will render only the changed DOM elements. The problem here was the dispatch is being called every time when the Layout page is rendered and the state gets updated and the particular DOM hence gets updated. This gives an impression of "reload" effect. Dispatching the action conditionally will do the trick. Dispatch only when the state changes.
I have inherited a code base that makes frequent use of React.cloneElement(this.props.children, {}) in the render() method of container components. Like this:
import React from 'react';
class App extends React.Component {
render() {
<div>
{this.props.children && React.cloneElement(this.props.children, {})}
</div>
}
}
...
import {Route} from 'react-router'
import App from '..';
import SomePureComponent from '..';
<Route
component={App}
path='/'
>
<Route
component={SomePureComponent}
path='/test'
/>
</Route>
Isn't this an anti-pattern? It was my understanding that calling functions like React.cloneElement() will return a new instance on each render, alerting the virtual DOM of new changes it must reconcile, and thus leaving PureComponent ineffective at minimizing re-renders. Am I misunderstanding something about Virtual DOM reconciliation?
I am trying to redirect to another path after receiving some information from the server, but It seems I can only use within , which I am not able to do.
the flow looks like this
1. client send socket message asking for a path
2. server look up a path, and send it back to client
3. client store the path into redux
4. redirect to the path
How can I achieve this?
Here's two ways you can redirect using react-router-dom, here's an example.
I suggest taking a look at the react-router docs here https://reacttraining.com/react-router/web/guides/philosophy
Method 1:
If you have a component that wraps your whole application use render and pass props for its arguments example.
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Route.md#render-func
//Wrapping component
import {BrowserRouter as Router, Route} from 'react-router-dom'
//Import your store ex:
{store} from '/path/to/store'
import Child from './components/child'
import Navbar from './components/navbar'
import Footer from './components/footer'
class App extends React.Component {
render() {
return(
<Provider store={store}>
<Router>
<div className="root">
<Navbar />
<Route exact path="/child" render={props => <Child {...props}/>}
//More routes.....
<Footer />
</div>
</Router>
</Provider>
)
}
}
//Child component
class SomeComponent extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
socket.on('redirect', url => {
//dispatch your action to update store then redirect
this.props.history.push(url)
})
}
render() {
//some html
}
}
Method 2:
Store the redirect path in the store, and check if not null, you can use middleware, but for this example I'll use a simple if statement inside the component, the drawback using this method is you will need to dispatch an action after the redirect(you may run into some redirect issues if you don't) so it may be useful to wrap the redirect inside a method, and dispatch another action to set it back to null.
import {Redirect} from 'react-router-dom'
class SomeComponent extends React.Component {
render() {
if(this.props.redirectPath) return <Redirect to={this.props.redirectPath} />
else //Render your component
}
}
I need to pass props to component using router.
Here's my code:
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import AppBarTop from './appbar/AppBarTop';
import Login from '../pages/login/Login';
import {BrowserRouter as Router, Route} from 'react-router-dom';
class App extends Component {
render() {
const { isAuthenticated } = this.props;
return (
<Router>
<div>
<AppBarTop isAuthenticated={isAuthenticated} />
<div className="content">
<Route path="/login" isAuthenticated={isAuthenticated} component={Login} />
</div>
</div>
</Router>
);
}
}
As you can see, isAuthenticated the prop i want to pass to Login component.
class Login extends Component {
constructor(props) {
super(props);
console.log(props);
}
render() {
return (
<LoginForm />
);
}
}
export default connect(null) (Login);
When i log the props the isAuthenticated prop is not there. What i'm doing wrong? How can i pass the prop correctly?
I followed the docs and also other discussions. From my understanding it should work.
The version of react-router and react-router-dom is 4.0.0
Pass it like this:
<Route
path="/login"
render={(props) => <Login {...props} isAuthenticated={isAuthenticated}/>}
/>
It should be available by this.props.isAuthenticated in Login Component.
Reason of {...props}:
If we don't write this then only isAuthenticated will get passed to Login component, all other values that router passes to component, will not be available inside Login component. When we write {...props} then we are passing all the values with one extra value.
And instead of using component with router use render method.
As per DOC:
Component:
When you use component (instead of render or children, below) 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 attribute, 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.
Render:
This allows for convenient inline rendering and wrapping without the
undesired remounting.