React access a variable from child component - javascript

I have a main layout for my app and all components are rendered inside it, but this main layout needs to access a variable PageTitle from it's child components, I have looked in react documentation and come up with React Context, but I could'nt figure out how to use it in this case, sorry I am new to React.
Every component rendered by routes has a variable called PageTitle, the MainLayout should use this variable to render current page title on the top of the app, these are all functional components.
<MainLayout>
<Route path='/' exact component={HomePage} />
<Route path='/invoice' exact component={Invoice} />
</MainLayout>
Update:
I can create a context and store the value like this, but I couldn't figure out how to change it in child components.
Also I think this is a bit overkill, and there should a better solution.
export const AppContext = React.createContext({ PageTitle: 'Home' });
<AppContext.Consumer>{value=>value.PageTitle}</AppContext.Consumer>

Related

Passing props to dynamically rendered components in React

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
)

Why am I outside the <Router> after rendering inside it?

Im building a Reactjs App. I don't understand why doing this is wrong and lead me to this error :
Error: Invariant failed: You should not use <Redirect> outside a <Router>
Here is the code :
A simple router in index.js
ReactDOM.render(
<Router>
<Switch>
<Route exact path="/" component={(props) => <AppContainer><HomePage {...props}/></AppContainer>}/>
<Route exact path="/register" component={(props) => <AppContainer><RegisterPage1 {...props}/></AppContainer>}/>
</Switch>
</Router>, document.getElementById('root')
);
The AppContainer is just a simple div with id :
export default function AppContainer({children}) {
return (
<div id="appContainer">{children}</div>
);
}
Inside RegisterPage1, I trigger a Render on "appContainer" after some process. This should render a new component inside the router.
ReactDOM.render(<RegisterPage2 data={res}/>, document.getElementById('appContainer'));
Inside RegisterPage2, I'm using and I get the above error.
if (redirectHome) return (<Redirect to={"/"}/>)
else {
return (
//RegisterPage2Components
)
}
Since I'm rendering inside the Router, I dont understand why I can use Redirect in RegisterPage1 and cannot in RegisterPage2. I checked in RegisterPage2 and the router is still here (by adding some random text between the Router and the Switch).
If rendering like this is wrong, what is the correct way without adding a new route ?
Codesandbox :
https://codesandbox.io/s/autumn-leftpad-7s5sy?file=/src/index.js
Since I'm rendering inside the Router, I dont understand why I can use
Redirect in RegisterPage1 and cannot in RegisterPage2.
React Dev Tools can help you here
As you can see at the bottom part of the image, RegisterPage2 is not inside a Router component. Compare it with the topmost portion of the image, that React App is wrapped inside a BrowserRouter. You will realize why these errors occur.
If rendering like this is wrong, what is the correct way without
adding a new route?
It is with great difficulty this requirement you are asking. It will be hard to traverse between two React Apps using react-router if they don't have a common Router parent. I suggest you resort to the classic server side http request (example anchor tags or window.href.location) instead of using Single Page Application technologies for this feature you are developing
That issue is cause by this line
ReactDOM.render(
<RegisterPage2 data={res}/>,
document.getElementById('appContainer')
);
This line is supposed to render the RegisterPage2 component as the Root component of your React app.
And in the RegisterPage2 you are using Redirect by assuming the RegisterPage2 has already a parent component which has already use the react- router-dom Router Component. Because It's render as the Root Component without is beeing wrap in the Router Component that is the reason you are getting that error.

React router - pass function to children

I´m developing a react-app using react-router, redux and redux-saga with the following structure:
App.js: Place where all my routes are located
<Switch>
<Route path="/" exact component={Index}/>
<Route path="/user" exact component={Userlist}/>
<Route path="/user/:id" exact component={Userdetail}/>
</Switch>
Userlist.js:
Stateful component connected to redux and fetching data from my backend. Data is listed in a table and has a link to Userdetail component.
In the Userlist component all state management has to be done (e. g. update/delete an user)
After clicking on one row in the table the Userdetail component is rendered (I pass the user data via Router-Link state to my component).
<Link to={{
pathname: `/user/${props.user._id}`,
state: {
user: user
}
}} className="stretched-link"/>
Passing the data to my userdetail list is working as it should by using state and access it via props.location in my userdetail component.
Userdetail.js:
In this component all other data of the user is displayed. Also there are two buttons which handle the update and delete part. The reference of these two function should be passed as props from Userlist to Userdetail component.
Now I´m coming to my question:
How can I pass function references from Userlist.js to the corresponding child component (Userdetail.js)?
I know there is a way to pass multiple props to a route using the render method instead of the component, but struggle to implement this according to my app structure. If possible I want to maintain all my routes in App.js or a separate file.
Thank you very much in advance

React build removes name of component class

I'm making a fullscreen rotating noticeboard in React with create-react-app.
The App component contains many Screen components, each one containing one child component (named such as WiFiPassword, OpenTimes, Events...) - and the Screen component checks if the current item from App is the name of it's child, and if it is then it shows that screen, if not then it hides it.
This is the code where the values are checked:
class Screen extends Component {
componentDidUpdate() {
const active = this.props.active; // The active item, from parent state
const elemName = this.props.children.type.name; //The name of the child class
...
if(active === elemName){
//If we have a match, then show this screen
}
}
...
}
This works really well when running in development.
When I run npm run build and the process is finished, I load up the app and it doesn't work - it appears that the build process scrubs the class names from elements, which then means nothing is ever shown.
When I console.log() both the active prop and the child class name I get this:
active: WiFiPassword
elemName: t
Whatever screen it is on, elemName, which is the name of Screen's child, is always t
Is there a way to retain the name of a component in react through the build process?
So I found a way around this: instead of relying on the class name, I instead imported every one of my <Screen> elements using a screens/index.js file:
...
export {default as Welcome} from './Welcome'
export {default as WiFi} from './WiFi'
...
Then imported that index, file with each screen available to me:
import * as Screens from './screens'
Then I use whatever screen I want, depending on the same logic from within App:
const Component = Screens[this.state.slide];
<Component/>
It looks like, you should switch to use react-router-dom, because you will have a good way to manage your screens. There is a good component Switch, that shows one component at the time:
import { Switch, Route } from 'react-router'
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
</Switch>
more information here: https://reacttraining.com/react-router/web/api/Switch

react router this.props.location

I need a help with react-router v2+
I have to change class of navbar when route changed
for example for route /profile className will be "profile-header"
I tried to use this.props.location in navbar component but it shows undefined
Hope your help
Your navbar component (as you described it in your question) is probably not the route component, right? By route component I mean the one that you use in your react-router configuration that is loaded for a specific route.
this.props.location is accessible only on such route component, so you need to pass it down to your navbar.
Let's take an example:
Your router config:
<Router>
<Route path="/" component={App}>
// ...
</Router
Route component App:
class App extends React.Component{
// ...
render() {
return <Navbar location={this.props.location}/>
}
}
There could be a scenario where you may not have access to props.location to pass to the nav component.
Take for example - We had a header component in our project which was included in the routing switch to make it available to all routes.
<Switch>
<Fragment>
<Header/>
<Route path='..' component={...}/>
<Route path='..' component={...}/>
</Fragment>
</Switch>
In the above scenario there is no way to pass the location data to the Header component.
A better solution would be to us the withRouter HOC when a component is not being rendered by your router.
You will still have access to the router properties history, match and location when you wrap it in the withRouter HOC:
import { withRouter } from 'react-router-dom'
....
....
export default withRouter(ThisComponent)
react-router v4
From documentation:
<Route> component property should be used, when you have an existing component. <Route> render property takes an inline function, that returns html.
A <Route> with no path will always match.
Based on this, we can make a <Route> wrapper to any level of your html structure. It will always be displayed and have access to the location object.
As per your request, if a user comes to /profile page, the <header> will have profile-header class name.
<Router>
<Route render={({ location }) =>
<header className={location.pathname.replace(/\//g, '') + '-header'}>
// header content...
</header>
<div id="content"></div>
<footer></footer>
} />
</Router>
I couldn't solve it with the solutions given here and here is what worked for me:
I imported history into my component and assigned history.location.pathname to a variable which I later used for dynamic style manipulation.
In case you are rendering the component with pre-defined location.state values, first set your state with props.location.state then use your state data in your elements.

Categories