can't use child props in react - reactjs

I have a problem with the code - I do not understand how to solve it - I tried for a few hours - it does not work.
I do not know what's going on. I'm just trying to access from a component of courses - and a component of a single course.
I get this error:
This is the code I wrote down so far - not a long code.
course.js:
import React, { Component } from 'react';
class Course extends Component {
render() {
return (
<div>
<h1>this.props.location.title</h1>
<p>You selected the Course with ID: {this.props.match.params.id}</p>
</div>
);
}
}
export default Course;
courses.js:
import React, { Component } from 'react';
import './Courses.css';
import { Link } from "react-router-dom";
class Courses extends Component {
state = {
courses: [
{ id: 1, title: 'Angular - The Complete Guide' },
{ id: 2, title: 'Vue - The Complete Guide' },
{ id: 3, title: 'PWA - The Complete Guide' }
]
}
render() {
return (
<div>
<h1>Amazing Udemy Courses</h1>
<section className="Courses">
{
this.state.courses.map(course => {
return (
<Link
key={course.id}
to={{
pathname: this.props.match.url + "/" + course.id,
title: course.title
}}>
<article className="Course">{course.title}</article>
</Link>
)
})
}
</section>
</div>
);
}
}
export default Courses;
app.js:
import React, { Component } from 'react';
import Courses from './containers/Courses/Courses';
import Users from './containers/Users/Users';
import { NavLink, BrowserRouter, Switch, Route } from "react-router-dom";
import './App.css';
class App extends Component {
render() {
return (
<BrowserRouter>
<div className="App">
<header>
<nav>
<ul>
<li>
<NavLink
to="/users"
exact
activeClassName="my-active"
activeStyle={{
color: '#fa923f',
textDecoration: 'underline'
}}>Users
</NavLink>
</li>
<li>
<NavLink to={{
pathname: '/courses',
hash: '#submit',
search: '?quick-submit=true'
}}>Courses
</NavLink>
</li>
</ul>
</nav>
</header>
<Switch>
<Route exact path="/">
</Route>
<Route path="/users">
<Users />
</Route>
<Route path="/courses">
<Courses />
</Route>
</Switch>
<ol style={{ textAlign: 'left' }}>
<li>Pass the course ID to the "Course" page and output it there</li>
<li>Pass the course title to the "Course" page - pass it as a param or score bonus points by passing it as query params (you need to manually parse them though!)</li>
<li>Load the "Course" component as a nested component of "Courses"</li>
<li>Add a 404 error page and render it for any unknown routes</li>
<li>Redirect requests to /all-courses to /courses (=> Your "Courses" page)</li>
</ol>
</div>
</BrowserRouter>
);
}
}
export default App;

You're trying to access the props that come from react router, with the syntax that you're using on your app.js file you're not passing any props. To be able to pass react router props to your component you can use this syntax instead:
<Route path="/courses" component={Courses} />
So everytime you access a link that takes you to the courses you will have available in you component all the props that are coming from react router.
Here's a sandbox with an example of a component that uses that syntax to obtain all the router props vs another that uses the syntax you're using and it does not have access to the router props:
Sandbox with example
I'm using functional components but with your example doing this.props will give the router props including the match object. With your courses component being a class I think this is the easiest way to do it, you can also wrap the component in a withRouter HOC to accomplish the same thing:
WIth router docs

You need to set props in your child components for them to be available, like so...
<Courses
match={this.props.match}
/>
Right now, all you have is <Courses />, without passing an props. So in the Courses class, of course you'll get this message: Cannot read property 'url' of undefined on your call of <Link ....this.props.match.....>.
But if you pass along the match prop, like match={this.props.match}, there should no longer be this error.
Take a look at the ReactJS documentation on how props work...
However, elements can also represent user-defined components:
const element = <Welcome name="Sara" />;
When React sees an element representing a user-defined component, it passes JSX attributes and children to this component as a single object. We call this object “props”.
Source: ReactJS.org: Components and Props

Related

how to show paginated table in react

I want to render a table in React on a page. Table has contains a lot of rows so I want to apply pagination. Like when someone clicks on page 2 link(/open_alerts/?page=2), table fetches next rows while keeping the same page and just changing the data. But i am facing problem, i can not know in my AlertsTable component the value of page parameter. i saw something useParams hook but this is not allowed in class component. How can I achieve the desired thing?
Home page is at / and it contains link to /open_alerts/ page.
App.js:
<Router>
<Switch>
<Route exact path="/">
<Link to="/open_alerts/">Open Alerts</Link>
<div className="Charts">
<AlertsChart id="alerts-chart"/>
<RegionsChart id="regions-chart"/>
</div>
</Route>
<Route path="/open_alerts/">
<Link to="/">Home page</Link>
<AlertsTable />
</Route>
</Switch>
</Router>
How do i know in AlertsTable component the page number so that i may use page number in my API call to server to get the paginated response?
alerts_table.js:
export class AlertsTable extends React.Component {
.
. // code here
.
render() {
return (
<div>
<h1>
Open alerts
</h1>
{ !this.state.isfetched ? <p>Fetching open alerts</p>:
<div>
<AlertsRenderer alerts={this.state.alerts}/>
<PaginatedButton pages={this.state.pages_count} />
</div> }
</div>
);
}
}
paginated_button.js:
export function PaginatedButton(props) {
return (
<Router>
{props.pages.map(page_no => (
<button>
<Link exact to={"/open_alerts/?page="+(parseInt(page_no)+1)}>{page_no+1}</Link>
</button>
))}
</Router>
)}
AlertsRenderer is just a functional component that gets alerts from props and displays using HTML table tags.
useParams wouldn't help you anyway since that is the route's match params, nothing to do with an URL's query string parameters.
You will have to process the query string, which can be accessed from the location route prop.
{
key: 'ac3df4', // not with HashHistory!
pathname: '/somewhere',
search: '?some=search-string',
hash: '#howdy',
state: {
[userDefined]: true
}
}
Use URLSearchParams to process the query string.
const search = this.props.location.search;
const queryParams = Object.fromEntries(new URLSearchParams(search));
const search = "?page=2";
const queryParams = Object.fromEntries(new URLSearchParams(search));
console.log(queryParams);
You can access the route props by a number of ways, but with class-based components they are either directly passed when the component is directly rendered by a Route component on the render or component props, or if you decorate the component with the withRouter Higher Order Component.
In your case it seems that <AlertsTable /> is not directly rendered by a Route component so you should decorate AlertsTable with withRouter and access the location prop that is passed.

state undefined in location react router dom link

component 1 passing the state through link's to
<Link to={{
pathname: "/Dashboard",
state: { color: "red" }
}}>
component 2 receiving the state
import React from 'react';
export interface DashboardProps {
location: any;
}
export interface DashboardState {
}
class Dashboard extends React.Component<DashboardProps, DashboardState> {
render() {
console.log("props", this.props.location);
return (
<h1>dashboard</h1>
);
}
}
export default Dashboard;
result
main component render
public render() {
return (
<HashRouter>
<div>
<Route path="/Dashboard" render={(props: any) => (<Dashboard {...props} />)} />
</div>
</HashRouter >
)
}
I believe I don't need to wrap my dashboard(component to receive state through link react router dom)
with "withRouter"
what a pity ! HashRouter doesn't support location.state:
You can see here https://github.com/ReactTraining/react-router/issues/7628 or https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/HashRouter.md
Hashrouter doesn't support location.key and location.state. Go through the documentation here https://v5.reactrouter.com/web/api/HashRouter

Passing props from parent to sibling in React

I am recreating a simple React app that I have already created in Angular. The React app has two components: one (menus.js) for a side menu and a second (content.js) that will display the content from each item in the menu when each link is clicked (just like an iframe of sorts). In the App.js I am making a REST API call to populate the state for the menus.js component. Note that both components are in the App.js as follows:
App.js
import React,{Component} from 'react';
import Menus from './components/menus';
import Content from './components/content';
class App extends Component {
state = {
menus: []
}
componentDidMount(){
fetch('api address')
.then(res => res.json())
.then((data)=> {
this.setState({menus: data})
})
.catch(console.log)
}
render(){
return (
<div>
<div><Menus menus={this.state.menus} /></div>
<div><Content /></div>
</div>
);
}
}
export default App;
here is the menu.js component; it takes a prop (menus) from App.js and builds the menu links with items from it:
import React from 'react';
import { BrowserRouter as Router, Link,} from "react-router-dom";
const Menus = ({ menus }) => {
return (
<Router>
<div>
<center><h1>Lessons</h1></center>
{menus.map(menu => (
<li key={menu.lesson}>
<Link to={`/lesson/${menu.lesson}`}>{menu.lessonName}</Link>
</li>
))}
</div>
</Router>
);
};
export default Menus;
Here is what I need - how do I pass items from the same prop (from App.js) to the content component? FYI - I need this to happen each time a link in the menu in menu.js is clicked (which is why a key is used in the list The simple idea is content will update in the content component each time a menu link in the menu component is clicked.
**content.js**
import React from 'react'
const Content = () => {
return (
<div>{menu.content}</div>
)
};
export default Content
Based on your description of the problem and what I can see of what you've written, it seems to me like you are trying to build an application where the menu persists, but the content changes based on menu clicks. For a simple application, this is how I would structure it differently.
<ParentmostComponent>
<MenuComponent someProp={this.state.some_data} />
<Switch>
<Route path={"/path"} render={(props) => <Dashboard {...props} someProp={this.state.some_other_data_from_parents} />
</Switch>
</ParentMostComponent>
This would allow the menu to always stay there no matter what the content is doing, and you also won't have to pass the menu prop to two components.
In your menu.js, attach the menu object to the Link
...
{menus.map(menu => (
<li key={menu.lesson}>
<Link to={{
pathname: `/lesson/${menu.lesson}`,
state: menu
}}> {menu.lessonName} </Link>
</li>
))}
...
In your content.js receive the menu like this:
import React from 'react'
const Content = () => {
console.log(props.location.state.menu.content);
return (
<div>{props.location.state && props.location.state.menu.content }</div>
)
};
export default Content
Read more here
Your example uses React Router, so this answer uses it as well.
First of all, move the Router up the hierarchy from Menus to App to make the router props available to all components. Then wrap your Content inside a Route to render it conditionally (i.e. if the path matches "/lesson/:lesson"):
class App extends Component {
state = {
menus: [
{
lesson: '61355373',
lessonName: 'Passing props from parent to sibling in React',
content: 'I am recreating a simple React app…'
},
{
lesson: '27991366',
lessonName: 'What is the difference between state and props in React?',
content: 'I was watching a Pluralsight course on React…'
}
]
}
render() {
const { menus } = this.state
return (
<Router>
<div>
<div><Menus menus={menus}/></div>
<Route path="/lesson/:lesson" render={({ match }) => (
<div><Content menu={menus.find(menu => menu.lesson === match.params.lesson)}/></div>
)}>
</Route>
</div>
</Router>
)
}
}
With the help of the render prop, you can access the router props (in this case match.params.lesson) before rendering your child component. We use them to pass the selected menu to Content. Done!
Note: The basic technique (without React Router, Redux etc.) to pass props between siblings is to lift the state up.

How to pass state to components rendered by Router

I am making a React portfolio and one of the components will be an image gallery (needless to say I am a newbie to React, I wonder, or I'd not be asking this question). To do so, I will use a package (react-image-gallery), which allows me to customize several items. I already installed, and it works well, but I need to customize it - hide thumbnails and "play" and "fullscreen" buttons.
According to the documentation, it is possible to do so via the state. The issue is that I am failing to do so because the navigation of the App uses a router, and although I tried hard, I could not make the state pass into the component placed on Router. This is the code of the main component (the equivalent to App) which is not working:
import "./styles.css";
import {
HashRouter,
NavLink,
Route
} from "react-router-dom";
import React, { Component } from "react";
import Development from "./Development";
import Home from "./Home";
import Intro from "./Intro";
import Media from "./Media";
import StudyCase from './StudyCase';
import Stuff from "./Stuff";
class Main extends Component {
constructor() {
super();
this.state = {
showThumbnails: false
};
}
render() {
return (
<HashRouter>
<div>
<ul className='top-menu'>
<li>
<NavLink className='navs hvr-pulse' to='/'>
Home
</NavLink>
</li>
<li>
<NavLink className='navs hvr-pulse' to='/dev'>
Development
</NavLink>
</li>
<li>
<NavLink className='navs hvr-pulse' to='/media'>
Media
</NavLink>
</li>
<li>
<NavLink className='navs' to='/study-case'>
Study Case
</NavLink>
</li>
</ul>
<span className='bottom-slider'></span>
<div className='content'>
<Route exact path='/' component={Intro} />
<Route path='/dev' component={Development} />
<Route path='/media' component={Media} />
<Route path='/study-case' render={props => (<StudyCase {...this.state} showThumbnails={this.state.showThumbnails}/>)}/>
</div>
</div>
</HashRouter>
);
}
}
export default Main;
Can some of the colleagues point the error I am doing to me? Thanks in advance for the availability.
After some hours looking for the solution, I nailed it, and it was pretty basic, so a newbie could find the answer. It happened that the <StudyCase/> component had a component itself, and the state should live there. I was led to error by the rationale of a gallery package I was working with before, where state had to be manipulated in the same page of the router. Here is the code of the <StudyCase/> component, with the working state, if someone may need at some point:
import ImageGallery from 'react-image-gallery';
import React from 'react';
class StudyCase extends React.Component {
constructor() {
super();
this.state = {
showThumbnails: false,
showPlayButton: false,
showFullscreenButton: false,
showGalleryFullscreenButton: false,
};
}
render() {
return (
<ImageGallery
items={images}
showThumbnails={this.state.showThumbnails}
showPlayButton={this.state.showPlayButton && this.state.showGalleryPlayButton}
showFullscreenButton={
this.state.showFullscreenButton && this.state.showGalleryFullscreenButton
}
/>
// <ImageGallery
// items={images}
// showThumbnails={this.state.showThumbnails} />;
);
}
}
const images = [
{
original: 'https://picsum.photos/id/1018/1000/600/',
thumbnail: 'https://picsum.photos/id/1018/250/150/',
},
{
original: 'https://picsum.photos/id/1015/1000/600/',
thumbnail: 'https://picsum.photos/id/1015/250/150/',
},
{
original: 'https://picsum.photos/id/1019/1000/600/',
thumbnail: 'https://picsum.photos/id/1019/250/150/',
},
];
export default StudyCase;

React-router custom prop not passing to component. ternary operator not working correctly

In React i have my App.js page where i keep my states. I'm importing user1.js component to App.js, and in user1.js component i have a link button that takes me to path /user2.
When i click the button, React will set state property called testValue to true and in user2.js page ternary operator should choose the first value - test works because of that. But for some reason it does not work.
Any help?
APP.JS
import React, { Component } from 'react';
import './App.css';
import User1 from './components/user1';
class App extends Component {
constructor(props){
super(props);
this.state = {
testValue:false
};
}
change = () => {
this.setState({
testValue:true
},() => {
console.log(this.state.testValue)
});
}
render() {
return (
<div className="App">
<User1 change={this.change}/>
</div>
);
}
}
export default App;
USER1.JS
import React from 'react';
import { BrowserRouter, Route, Switch, Link } from 'react-router-dom';
import User2 from './user2.js';
const User1 = (props) => {
return(
<BrowserRouter>
<div>
<Link to ="/user2">
<button onClick={props.change}>Next page</button>
</Link>
<Switch>
<Route path="/user2" exact component={User2}/>
</Switch>
</div>
</BrowserRouter>
); // end of return
};
export default User1;
USER2.JS
import React from 'react';
const User2 = (props) => {
console.log(props)
return(
<div>
{props.testValue ?
<p>test works</p>
:
<p>test does not work</p>
}
</div>
);
};
export default User2;
This is what i expected - test works
This is what i got - test does not work
You want to pass a custom property through to a component rendered via a route. Recommended way to do that is to use the render method.
<Route path="/user2" exact render={(props) => <User2 {...props} testValue={true} />} />
I think a valid inquiry here would be what are you wanting to pass through as an extra prop? whats the use case here? You may be trying to pass data in a way you shouldn't (context would be nice :D).

Resources