React Component does not render when clicking CardActionArea from Material UI - reactjs

I'm running into a weird issue that I've never run into before.
I'm using Material UI components, specifically CardActionArea paired with
Redirect from react-router-dom
Upon clicking the CardActionArea I want to redirect my users to a detail screen of the component they just clicked.
The detail view sometimes renders and sometimes it doesn't. For example, if I click on the CardActionArea the detail view does not render, but if I navigate directly to the URL, the detail view does render.
This is the relevant code:
// Dashboard.js
return (
<Grid container spacing={40} className={classes.root}>
<TopMenu></TopMenu>
<Router>
<Route exact path="/dashboard/v/:videoId" component={VideoDetail} />
</Router>
<Router>
<Route exact path="/dashboard" component={(YouTubeVideoGallery)} />
</Router>
</Grid>
);
The CardActionArea is here:
constructor(props) {
super(props);
this.state = {
redirect: false
};
this.handleCardActionClick = this.handleCardActionClick.bind(this);
}
handleCardActionClick = () => {
this.setState({redirect: true});
}
render() {
const { classes } = this.props;
const date = moment(this.props.video.publishedAt);
if (this.state.redirect) {
return (<Redirect to={`/dashboard/v/${this.props.video.id}`} />)
}
return (
<Card className={classes.card}>
<CardActionArea onClick={this.handleCardActionClick}>
<CardHeader
title={
(this.props.video.title.length > 21) ?
this.props.video.title.substr(0, 18) + '...' :
this.props.video.title
}
subheader={`${date.format('MMMM DD[,] YYYY')} - Views: ${this.props.video.viewCount}`}
/>
<CardMedia
className={classes.media}
image={this.props.video.thumbnails.medium.url}
title={this.props.video.title}
/>
</CardActionArea>
</Card>
);
}
I'm not really sure what the problem is.

First thing handleCardActionClick is an arrow function so you no need to do binding in constructor. That can be removed
To redirect on onclick do something like below
render(){
const url = `/dashboard/v/${this.props.video.id}`;
return(
<div>
{this.state.redirect && <Redirect to={url} />}
</div>
)
}

Related

React JS refresh page every time clicking on menu item using route

I am new to React JS and I am currently building a simple application. I am using Route in order to navigate between components and everything work fine, but if I am on a page and I click again in the menu to navigate to the page, it doesn't refresh its content.
I just want the component to refresh its content every time I click on the item menu.
This is my sidebar class:
class Sidebar extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Router>
<Route render={({ location, history }) => (
<React.Fragment>
<SideNav
onSelect={(selected) => {
const to = '/' + selected;
if (location.pathname !== to) {
history.push(to);
}
}}>
<SideNav.Toggle />
<SideNav.Nav>
<NavItem eventKey="Cars">
<NavIcon>
Cars
</NavIcon>
</NavItem>
<NavItem eventKey="Bicycles">
<NavIcon>
Bicycles
</NavIcon>
</NavItem>
</SideNav.Nav>
</SideNav>
<main>
<Switch>
<Route exact path="/" component={props => <Home />} />
<Route
exact path="/Cars"
render={() => !isAllowed ?
<Home /> :
<Cars/>
} />
<Route
exact path="/Bicycles"
render={() => !isAllowed ?
<Home /> :
<Bicycles />
} />
</Switch>
</main>
</React.Fragment>
)}
/>
</Router>
)
}
}
This is my Cars Component class:
import React, { Component } from 'react';
class Cars extends Component {
render() {
return (
<div style={{ textAlign: 'center', marginLeft: '295px' }} >
<form>
<h1>Hello</h1>
<p>Enter your car name:</p>
<input
type="text"
/>
</form>
</div>
)
}
}
export default Cars;
For ex. if I text something in input and after that I click on the item menu, I want that input to be refreshed.
In order to "refresh" (or in React world called Re-render) the content of the component you need to change it's state, and that is how React works. As I can see you don't have any state in your component so if you can specify what you wanna "refresh" we can help you.
The heart of every React component is its “state”, an object that determines how that component renders & behaves. In other words, “state” is what allows you to create components that are dynamic and interactive.
Quick example from somewhere on the internet :
import React from 'react';
class Person extends React.Component{
constructor(props) {
super(props);
this.state = {
age:0
this.incrementAge = this.incrementAge.bind(this)
}
incrementAge(){
this.setState({
age:this.state.age + 1;
});
}
render(){
return(
<div>
<label>My age is: {this.state.age}</label>
<button onClick={this.incrementAge}>Grow me older !!<button>
</div>
);
}
}
export default Person;
The age of inside of the label is being Re-rendered (or "refresh") every time when the user clicks on it since its state is changing.
Here is an official documentation and I would recommend you read it, it will clarify a lot of issues you are facing.
https://reactjs.org/docs/state-and-lifecycle.html

Adding a Link as a child of a Router with ReactDOM.render yields "You should not use <Link> outside a <Router>"

I am looking for a way to use ReactDOM.render to create a Link within a react router. The setup more or less looks like this:
const router = (
<div>
<Router>
<Route path="/map" component={Map}/>
</Router>
</div>
);
The relevant parts of Map.jsx look like this:
const MapPopup = () => {
return (
<Link to={`/map/add`} />
)
}
class Map extends React.Component {
componentDidMount() {
this.map = L.map('map')
//...stuff...
this.map.on('contextmenu', event => {
popup
.setLatLng(event.latlng)
.addTo(this.map)
.setContent(
ReactDOM.render(
MapPopup(),
document.querySelector('.leaflet-popup-content')
)[0]
)
.openOn(this.map)
})
}
render() {
return (
<React.Fragment>
<div id="map" />
</React.Fragment>
)
}
}
I am basically trying to add a Link to the map popup provided by leaflet (I can't use react-leaflet for this project). If I however return the MapPopup directly in the render function it works (obviously not in the popup but the Link does work this way).
<React.Fragment>
<div id="map" />
<MapPopup />
</React.Fragment>
Does anyone have an idea how I can tackle this rather unusual problem?
I am using "react-router-dom": "4.3.1".
This is the expected error since <Link> component expects ancestor component to be of router type (<BrowserRouter>, <MemoryRouter>, <Router> ... ), refer this thread for a more details.
For your scenario to circumvent this limitation ReactDOM.createPortal could be utilized instead of ReactDOM.render:
<Route
path="/popup"
render={() => (
<Popup>
<div>
Some content goes here
<Link to="/map"> Back to map</Link>
</div>
</Popup>
)}
/>
where
class Popup extends React.Component {
render() {
return ReactDOM.createPortal(
this.props.children,
document.querySelector("#link-render-div")
);
}
}
and
Here is a demo for your reference

My component is creating TypeError: Cannot read property 'click' of null

This problem is related with my Buy_Item Component. The Urban Outfitter logo at the top left is supposed to bring you back to the home page. It works completely fine with my other components, except this one. When I click it gives me:
class App extends Component {
render() {
return (
<BrowserRouter>
<div className="App">
<Header/>
<Route exact path="/" render = {() => <Featured/> } />
<Route path="/items-available" render = {() => <Items_Available item_info={<Item_Info/>}/> } />
<Route path="/buy-item" render = {() => <Buy_Item buy_item_info={<Buy_Item_Info/>}
item_info={<Item_Info/>}/> } />
<Footer/>
</div>
</BrowserRouter>
);
}
}
export default App;
Here's what the "Urban Outfitter" logo does in my Header Component:
<div id="nav_logo_container">
<Link to="/">
<picture>
<source media="(min-width:768px)" srcset={uo_logo} />
<source srcset={uo_logo_smaller_screen} />
<img id="nav_logo" src={uo_logo} alt="Urban Outfitters Logo" />
</picture>
</Link>
</div>
Here's my Github repo, if it helps: https://github.com/mattfrancis888/project_2/tree/item_info/src
You have it all wrong in your Buy_Item component
Never call setState inside the render() method as it could cause an infinite loop. This is because calling setState will always lead to a re-render unless shouldComponentUpdate returns false.
Try this...
const imgDic = {
0: item_1,
1: item_1_alt,
2: item_1_alt2,
3: item_1_alt3,
4: item_1_alt4,
5: item_1_alt5
};
class Buy_Item extends React.Component {
constructor(props) {
this.state = {
radioStatus: null,
currentImg: item_1
};
this.handleRadioClick = this.handleRadioClick.bind(this);
}
handleRadioClick(radioId) {
this.setState({
radioStatus: radioId,
currentImg: imgDic[radioId]
});
}
render() {
// the rest of render code goes here...
}
}

Prevent react route unmounting component on state change

I'm using react-router (v.4.3.1) to render the main part of my application and I have a drawer on the left side with the menu. When a button is toggled in the app header I'm changing the state of the collapsed variable so that the components re-render the css accordantly. My problem is this variable needs to be stored on the component rendering all my Route and when the component is updated Route is unmounting and mounting it's component.
I've already tried to provide a key to my Route but it's not working.
My code looks like this and the parent of this component is the one being updated which re-renders my Main component:
class Main extends Component {
constructor(props) {
super(props);
this.observer = ReactObserver();
}
getLayoutStyle = () => {
const { isMobile, collapsed } = this.props;
if (!isMobile) {
return {
paddingLeft: collapsed ? '80px' : '256px',
};
}
return null;
};
render() {
const RouteWithProps = (({index, path, exact, strict, component: Component, location, ...rest}) =>
<Route path={path}
exact={exact}
strict={strict}
location={location}
render={(props) => <Component key={"route-" + index} observer={this.observer} {...props} {...rest} />}/>
);
return (
<Fragment>
<TopHeader observer={this.observer} {...this.props}/>
<Content className='content' style={{...this.getLayoutStyle()}}>
<main style={{margin: '-16px -16px 0px'}}>
<Switch>
{Object.values(ROUTES).map((route, index) => (
<RouteWithProps {...route} index={index}/>
))}
</Switch>
</main>
</Content>
</Fragment>
);
}
}
I would like the Route just to update and not to unmount the component. is this possible?
you are having this issue due to defining RouteWithProps inside of render method. This causes React to unmount old and mount a new one each time render method is called. Actually creating component dynamically in the render method is a performance bottleneck and is considered a bad practice.
Just move the definition of RouteWithProps out of Main component.
Approximate code structure will look like:
// your impors
const RouteWithProps = ({observer, path, exact, strict, component: Component, location, ...rest}) =>
<Route path={path}
exact={exact}
strict={strict}
location={location}
render={(props) => <Component observer={observer} {...props} {...rest} />}/>;
class Main extends Component {
...
render(){
...
{Object.values(ROUTES).map((route, index) => (
<RouteWithProps key={"route-" + index} {...route} observer={this.observer}/>
))}
^^^ keys should be on this level
...
}
}

Timeout for component unmount

Okay, I have an idea about making comfortable and native page transitions with React. I made an example of how pages must transition between themselves - you can check it here.
And then I started to programming it with React. And I have stalled.
Let's say I have a component Scene:
export default class Scene extends React.Component {
constructor(props) {
super(props)
this.state = {
beforeUnmount: false
}
}
render() {
return (
<div
className={ classNames({ "scene-container": true, "scene-unmount": this.state.beforeUnmount })}
style={{ backgroundColor: this.props.color}}>
<h1 style={{ marginTop: "25%", textAlign: "center" }}> Scene { this.props.id } </h1>
{ this.props.children }
</div>
)
}
}
and a TestPage view that uses this Scene (AnotherTestPage is mostly the same):
export default class TestPage extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
<Helmet
title={`Scene ${this.props.params.id}`}
meta={[{name: 'description', content: 'Index page'}]}
/>
<Scene id={ this.props.params.id } color= {`#${this.props.params.color}`}>
<DefaultLink to="/" text="Go to Index" />
<DefaultLink to="/another" text="Go to Another" />
</Scene>
</div>
)
}
}
and the Router
export function createRoutes(history, store) {
return (
<Router>
<Route component={App}>
<Route path='/' component={IndexPage} />
<Route path='/forms' component={Forms} />
<Route path='/scenes/:id/:color' component={TestPage} />
<Route path='/another' component={AnotherTestPage} />
</Route>
</Router>
)
}
Before current view is being unmounted, there must be fade-out animation of Scene, and at the same time next view should fade-in. And my problem is i don't know how to do it, how to timeout unmount of the view. Will be very thanksful if anyone will help me!
Animations in React are done with the help of React ad-on: react-addons-css-transition-group
There's an example in React Router that accomplishes what you're trying to achieve (animation between page transitions): https://github.com/ReactTraining/react-router/tree/master/examples/animations

Resources