How to pass state to components rendered by Router - reactjs

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;

Related

React Link doesn't refresh page automatically

I am currently experiencing an issue similar to React Link doesn't refresh the page, however, the answer doesn't work well for my case.
See, I am currently using react-router to have a path called 'study/:id'. This :id variable will just be printed on the page
Here is the code for my BrowserRouter (App.js)
import React from 'react';
import './App.css';
import HomePage from './HomePage/HomePage';
import Study from './StudyPage/Study';
import {BrowserRouter as Router, Route, Switch } from 'react-router-dom';
function App() {
return (
<Router>
<Switch>
<Route path="/" exact={true} component={HomePage}/>
<Route path="/Study/:id" exact={true} component={Study} />
</Switch>
</Router>
);
}
export default App;
Inside the Study component itself, it basically just has a menubar and an indicator on which courseId are we in:
import React from 'react';
import './Study.css';
import Menubar from '../Menubar';
import Sidebar from './Sidebar';
import Chapter from './Chapter';
class Study extends React.Component{
constructor(props){
super(props);
this.state = {
courseId: this.props.match.params.id,
};
}
render(){
return(
<div id="studyWrapper">
<Menubar />
<h1>We are on course: {this.state.courseId}</h1>
</div>
)
}
}
export default Study;
In order for the user to navigate through the study pages, I use a menubar component like this (Menubar.js)
import React from 'react';
import './Menubar.css';
import { Nav } from 'reactstrap';
import { Dropdown, DropdownButton } from 'react-bootstrap';
import { Link } from 'react-router-dom';
class Menubar extends React.Component{
constructor(props){
super();
this.state = {
courses: [],
reload: false
}
}
async componentDidMount(){
const response = await fetch("/v1/courses/")
const body = await response.json();
this.setState({
courses: body
});
}
render(){
const {courses} = this.state
return (
<Nav className="navbar navbar-expand-md navbar-light menubarStyle fixed-top">
<div className="container-fluid">
<a className="navbar-brand logo" href="/">LOGO</a>
<div className="navbar-collapse">
<div className="navbar-nav">
<div className="dropdown nav-item">
<DropdownButton variant='Secondary' id="dropdown-basic-button" title="Browse Courses">
<Dropdown.Item as={Link} to={`/study/001`} >001</Dropdown.Item>
<Dropdown.Item as={Link} to={`/study/002`} >002</Dropdown.Item>
<Dropdown.Item as={Link} to={`/study/003`} >003</Dropdown.Item>
</DropdownButton>
</div>
</div>
</div>
</div>
</Nav>
)
}
}
export default Menubar
IRL, the study page basically looks like this
The problem
The problem that I am having is that, once I am in '/study/001' page already (just like the picture above). If I try to click on DropdownItem 002 from the menuBar, the URL will change to 'study/002', but the page won't change. It will not refresh.
The solution from React Link doesn't refresh the page basically says to use windows.location.reload() but that doesn't work in my case, if we do that, when I click on dropdownItem 002, the URL will change to 'study/002' for a moment, but then 'study/001' will refresh thus making the page back to 001
My question is, is there a way for us to refresh the page whenever the url is changed by link ?
Or if not, are there any other methods that I can use for this design? Maybe using links is not the right way in the first place?
Pardon the long post, I try to make it as clear as possible.
Thank you !
Inside your Study component you could use componentDidUpdate and compare the current props with the prevProps to check if the url has changed and then change the state, which should cause your component to update. More or less you would have this code:
componentDidUpdate(prevProps) {
if( this.props.match.params.id !== prevProps.match.params.id ){
this.setState({ courseId: this.props.match.params.id })
};
}

can't use child props in react

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

React Router NavLink does not render activeClassName when mapped

The activeClassName of React Routers NavLink does not work when mapped as shown in the following code. The view receives the links from its container as props where they are mapped. Everything works well, including the standard className, but the activeClassName doesn´t work. It works when I add a NavLink manually outside the mapping but I cant find the mistake. Any help appreciated.
import React from "react";
import { NavLink } from "react-router-dom";
import styles from './style.scss';
class NavView extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<ul>
{this.props.links.map(function(link, index) {
return <li key={index}> <NavLink to={link.slug} className="navLink" activeClassName="current" >{link.slug}</NavLink></li>
})}
</ul>
);
}
}
export default NavView
I'm quite certain that you simply need to add a / slash to the beginning of your routes' link slugs.
import React from "react";
import { NavLink } from "react-router-dom";
import styles from './style.scss';
class NavView extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<ul>
{this.props.links.map(function(link, index) {
return <li key={index}> <NavLink to={`/${link.slug}`} className="navLink" activeClassName="current" >{link.slug}</NavLink></li>
})}
</ul>
);
}
}
export default NavView;
React-router is quite specific and picky about matching URL patterns. If your route is /home, <NavLink/> will only match when the to prop is also /home, not home.

React Router fetch data on route change for a component without props

In Switch router scenario,how do we fetch fresh data from the server, after coming back to a already mounted component which doesn't have any props. I went through many posts before asking this. All posts suggest use componentWillRecieveUpdate. This life cycle hook will never be called if the component doesn't have any props. I even tried the getDerivedStatefromProps
What is best option to deal with scenarios like this.?
import React from "react";
import { render } from "react-dom";
// import react router
import { BrowserRouter as Router, Route, Switch, Link } from "react-router-dom";
class Application extends React.Component {
render() {
return (
<Router>
<div>
<Menu />
<div>
<Switch>
<Route path="/help" component={Help} />
<Route exact path="/" component={OverView} />
<Route component={OverView} />
</Switch>
</div>
</div>
</Router>
);
}
}
class Menu extends React.Component {
render() {
return (
<ul>
<li>
<Link to="/help">Help</Link>
</li>
<li>
<Link to="/">OverView</Link>
</li>
</ul>
);
}
}
class Help extends React.Component {
render() {
return (
<div>
<p>Some help</p>
</div>
);
}
}
class OverView extends React.Component {
constructor(props, context) {
super(props);
this.state = {};
}
getDerivedStatefromProps(prevprops,prevstate, ){
//Even this will also not be called
}
componentWillRecieveUpdate(newprops)
{
//I dont recieve a call to this when come back from help
}
render() {
//can we fetch data here and save in the state
// and re-render. Will this cause any issues
return <div> How to return fetch data</div>;
}
}
render(<Application />, document.getElementById("root"));
I have been digging in your code a little bit, please have a notice that your component is mounting every time, it's not one time mounted as you claimed.
please read the React Router doc to understand more.
You can see it in the Demo
class OverView extends React.Component{
constructor(props,context) {
super(props)
this.state = {}
}
componentDidMount() {
console.log('mounted');
}
render() {
console.log('render');
return(<div> How to return fetch data</div>)
}
}

Click on card then navigate to card page details in react

I created a page and show some card also some little details of cards
Now I want to click on any card navigate to this on show all details
How can I get to this page on react js ?
Is this need to Redux ?
I couldn't understand what you said completely, but you can use React-Router to navigate to different pages in Reactjs. To install it using npm, go to your Application directory and enter in command line npm install --save React-Router
Here is a simple code to navigate to different pages:
import React from "react";
import "./App.css";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
const Home=()=>
(<div>
<h2>Home</h2>
</div>
);
const About=()=>
(<div>
<h2>About</h2>
</div>
);
class App extends React.Component {
render() {
return (
<Router>
<div>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<Route exact path="/" component={Home}/>
<Route exact path="/about" component={About}/>
</div>
</Router>
);
}
}
export default App;
I hope it helped...
No need of Redux for your issue.
You just use React-router. you just create two component 1. Cards.js, 2. CardDetails.js and navigate to card details on click event of cards in Card.js
Cards.js
import React from "react";
import { withRouter } from "react-router";
class Cards extends React.Component {
goToCarddetails = (cardId) => {
localStorage.setItem("selectedCard", cardId);
this.props.history.push('/card-details');
// you can manage here to pass the clicked card id to the card details page if needed
}
render() {
return ( <div>
<div onClick = {()=>this.goToCarddetails('cardId1')}> card 1 </div>
<div onClick = {()=>this.goToCarddetails('cardId2')}> card 2 </div>
<div onClick = {()=>this.goToCarddetails('cardId3')}> card 3 </div>
</div>
)
}
}
export default withRouter(Cards);
With above on clicking any card you will be navigated to card details page.
CardDetails.js
import React from "react";
class CardDetails extends React.Component {
render() {
let selectedCardId = localStorage.getItem("selectedCard");
// you can get this cardId anywhere in the component as per your requirement
return ( <div>
card details here
</div>
)
}
}
export default CardDetails;
Note : I'm using react-router 4 here in example.

Resources