React: Switch out rendered component - reactjs

Quite new at this and have a question with something that I feel like should be an easy answer.
I essentially have a page that has 5 titles. When I click on a title, it will render that component. I have that, but I don't know how to then make the 5 titles go away? The component that is rendered just shows up below the titles and I want them to switch entirely. Have done a lot of googling and can't seem to get it right.
XYZ.js
import '../App.css';
import Button from 'react-bootstrap/Button'
import { BrowserRouter, Route, Router, Link } from "react-router-dom";
import Chat from './chat.js';
export class XYZ extends React.Component {
constructor(props) {
super(props);
this.state = {
showChat: false,
};
this._onChatClick = this._onChatClick.bind(this);
}
_onChatClick() {
this.setState({
showChat: !this.state.showChat,
});
}
render() {
return (
<div className="yyy">
<div className="xxx">
<div className="chatBody">
<Button className="chatTitle" onClick={this._onChatClick}>
Chat xxz
</Button>
{this.state.showChat ?
<Chat /> : null
}
</div>
<div>
....brevity
</div>
</div>
</div>
);
}
}
export default XYZ;
Thank you for any help!! Can supply additional files if necessary

Can' comment but I guess that you want <Button className="chatTitle"> to disappear when you click on it.
You need to either put it in the place of the null in your ternary operator or put another conditional rendering on top of the existing one, like:
{!this.state.showChat && <Button className="chatTitle">Chat xxz</Button>}

Related

How to make a react component with an image that can be "turned off" when placed in other components?

First React project here, so probably a very elementary question, but I'd appreciate any help as I'm not really sure how to phrase my question to search. I have this component:
import React, { Component } from 'react';
import SmallOgLogo from '../../../static/images/OG_logo.jpg';
export default class HeaderNavbar extends Component {
constructor(props) {
super(props);
this.state = {
showImage: true
}
}
render () {
return (
<div>
<div className='header-navbar'>
<div>
<img className='SmallOgLogo' src={SmallOgLogo}/>
</div>
)}
<h1>Title</h1>
</div>
</div>
);
}
}
This is a header for a project I'm working on. It is on every page in the React project. However, on the "title page" (first page users see with the option to "register" or "login"), I don't want it to display SmallOgLogo. On every other page, it will (hence the default setting of true. How can I make it so when I embed this component in another component, I can do something like ? Can anyone clarify? Thank you!

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 })
};
}

Component render triggered, but DOM not updated

I'm having problems with my first React application.
In practice, I have a hierarchy of components (I'm creating a multimedia film gallery) which, upon clicking on a tab (represented by the Movie component) must show the specific description of the single film (SingleMovieDetails).
The problem is that the DOM is updated only on the first click, then even if the SingleMovieDetails props change, the DOM remains locked on the first rendered movie.
Here's the code i wrote so far...
//Movie component
import React from "react";
import styles from "./Movie.module.scss";
import PropTypes from "prop-types";
class Movie extends React.Component{
constructor(props){
super(props);
this.imgUrl = `http://image.tmdb.org/t/p/w342/${this.props.movie.poster_path}`;
}
render(){
if(!this.props.size)
return <div onClick={this.props.callbackClick(this.props.movie.id)}
name={this.props.movie.id}
className={styles.movieDiv}
style={{backgroundImage: `url(${this.imgUrl})`}}></div>;
return <div onClick={() => this.props.callbackClick(this.props.movie.id)}
name={this.props.movie.id}
className={styles.movieDivBig}
style={{backgroundImage: `url(${this.imgUrl})`}}></div>;
}
}
Movie.propTypes = {
movie: PropTypes.any,
callbackClick: PropTypes.any
};
export default Movie;
SingleMovieDetails.js
import React from "react";
import styles from "./SingleMovieDetails.module.scss";
import Movie from "../Movie";
import SingleMovieDescription from "../SingleMovieDescription";
import MovieCast from "../MovieCast";
import SingleMovieRatings from "../SingleMovieRatings";
class SingleMovieDetails extends React.Component{
constructor(props){
super(props);
console.log(props);
this.state = props;
console.log('constructor', this.state.movie)
}
render(){
console.log('SMD', this.state.movie)
return (
<>
<div className={styles.container}>
<div className={styles.flayer}>
<Movie size={'big'} movie={this.state.movie}/>
</div>
<div className={styles.description}>
<SingleMovieDescription movie={this.state.movie}/>
<MovieCast></MovieCast>
</div>
<div className={styles.ratings}>
<SingleMovieRatings />
</div>
</div>
</>
);
}
}
export default SingleMovieDetails;
MovieCarousel.js
import React from "react";
import PropTypes from "prop-types";
import Movie from "../Movie";
import styles from "./MovieCarousel.module.scss";
import SingleMovieDetails from "../SingleMovieDetails";
class MovieCarousel extends React.Component {
constructor(props) {
super(props);
this.state = [];
this.callbackClickMovie = this.callbackClickMovie.bind(this);
}
callbackClickMovie(id) {
const singleMovieApi = `https://api.themoviedb.org/3/movie/${id}?api_key=b6f2e7712e00a84c50b1172d26c72fe9`;
fetch(singleMovieApi)
.then(function(response) {
return response.json();
})
.then(data => {
this.setState({ selected: data });
});
}
render() {
let details = null;
if (this.state.selected) {
details = <SingleMovieDetails movie={this.state.selected} />;
}
let counter = 6;
let movies = this.props.movies.map(el => {
let element = (
<Movie movie={el} callbackClick={this.callbackClickMovie} />
);
counter--;
if (counter >= 0) return element;
return;
});
let content = (
<>
<h2 className={styles.carouselTitle}>{this.props.title}</h2>
{movies}
{details}
</>
);
return content;
}
}
MovieCarousel.propTypes = {
children: PropTypes.any
};
export default MovieCarousel;
I would be really grateful if someone could help me. I have been on it for two days but I can't really deal with it
This is because in SingleMovieDetails component, you are storing the props values in state and not updating the state on props change. constructor will not get called again so state will always have the initial values.
You have two options to solve the issue:
Directly use the props values instead of storing data in state (preferred way). Like this:
class SingleMovieDetails extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<>
<div className={styles.container}>
<div className={styles.flayer}>
<Movie size={'big'} movie={this.props.movie}/>
</div>
<div className={styles.description}>
<SingleMovieDescription movie={this.props.movie}/>
<MovieCast></MovieCast>
</div>
<div className={styles.ratings}>
<SingleMovieRatings />
</div>
</div>
</>
);
}
}
Use getDerivedStateFromProps, and update the state value on props change.
Same issue with Movie component also, put this line in the render method, otherwise it will always show same image:
const imgUrl = `http://image.tmdb.org/t/p/w342/${this.props.movie.poster_path}`
And use this imgUrl variable.
your Problem is just related to one file: SingleMovieDetails.js
Inside the constructor you´re setting the component state to get initialized with the props (send to the component the first time)
But inside your render() method you are referencing that state again:
<Movie size={'big'} movie={this.state.movie}/>
All in all thats not completely wrong, but you need to do one of two things:
Add a method to update your component state with the nextPropsReceived (Lifecycle Method was called: will receive props, if you are using the latest version you should use: getDerivedStateFromProps)
preferred option: you dont need a state for the movie component, so just use the props inside the render function (this.props.movie)
afterwards you can also delete the constructor, because there is nothing special inside. :)
edit:
So, just to be clear here: Since you´re only setting the state once (the constructor is not called on every lifecycle update) you will always only have the first value saved. Changing props from outside will just trigger render(), but wont start the constructor again ;D

React - Route with same path, but different parameters change data on update

I am creating a page where you can see peoples profiles and all their items, which has pathname="/users/{their id}" and a menu where it can take you to your profile. But my problem is that when you go to a persons profile page, and then to another one, the pathname changes but the data does not get rendered it only changes the pathname and the data remains the same. In order to render the data, you would have to refresh the page and then it shows the new users data. How would I get it so you wouldn't have to refresh the page, so like they click on the user they want to go to, the pathname changes and renders the new data without the page refresh? Also, something happens when you refresh the page when on a user profile, it is supposed to return the users' email address, which it does when you first visit the page, but when you refresh the page it returns an error saying it can't find the email.
Here is the code for the menu part (link to my profile):
import { Meteor } from "meteor/meteor"
import React from "react";
import { withRouter, Link } from "react-router-dom";
import { SubjectRoutes } from "./subjectRoutes/subjectRoutes";
import AddNote from "./AddNote";
class Menu extends React.Component{
render(){
return(
<div>
<div className="scroll"></div>
<div className="menu">
<h1>Menu</h1>
<p><Link to="/">Home</Link></p>
<Link to="/searchNotes">Notes</Link>
<p><Link to="/addNote">Add a Note</Link></p>
<p><Link to={`/users/${Meteor.userId()}`} >My Profile</Link></p>
</div>
</div>
)
}
}
export default withRouter(Menu)
userProfile.js:
import { Meteor } from "meteor/meteor";
import React from "react";
import { withRouter } from "react-router-dom";
import { Tracker } from "meteor/tracker";
import Menu from "./Menu";
import RenderNotesByUserId from "./renderNotesByUserId"
class userProfile extends React.Component{
constructor(props){
super(props);
this.state = {
email: ""
};
}
logoutUser(e){
e.preventDefault()
Accounts.logout(() => {
this.props.history.push("/login");
});
}
componentWillMount() {
Meteor.subscribe('user');
Meteor.subscribe('users');
this.tracker = Tracker.autorun(() => {
const user = Meteor.users.findOne(this.props.match.params.userId)
this.setState({email: user.emails[0].address})
});
}
render(){
return(
<div>
<Menu />
<button onClick={this.logoutUser.bind(this)}>Logout</button>
<h1>{this.state.email}</h1>
<RenderNotesByUserId filter={this.props.match.params.userId}/>
</div>
)
}
}
export default withRouter(userProfile);
Sorry to make this question so long it's just a really weird problem that I am having which I can't seem to find any answers to online.
ComponentWillMount() only runs one time, before your component is rendered. You need to also use ComponentWillReceiveProps() in order to update your state when your props change.
Check out React Component Lifecycle
you can use useLocation in this situation.
let location = useLocation();
useEffect(() => {
dispatch(fetchDetail(id))
dispatch(fetchSuggestions(category))
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location]);

ReactJS Load Images on Click

Hello im trying to make an image changer. at first load i should get displayed the first image in the state. if click on the button the image should be changed to second and so on. the problem right now is get displayed both images with buttons
here is my bin : enter link description here
Because you display the 2 image in your renderer.
Heres an idea how to do it (with no animation, I dont know the CSSTransitionGroup component). Just replace your main.js code withthe following
import React from 'react'
import {render} from 'react-dom'
import { CSSTransitionGroup } from 'react-transition-group'
class FadeImage extends React.Component {
constructor(props) {
super(props);
this.state = {
images: [
'http://via.placeholder.com/350x150',
'http://via.placeholder.com/350x151'
],
currentImage: 0
};
}
fadeImage(e) {
e.preventDefault();
this.setState({currentImage: (this.state.currentImage + 1) % this.state.images.length})
}
render() {
return (
<div className="image">
<CSSTransitionGroup
transitionName="example"
transitionEnterTimeout={300}
transitionLeaveTimeout={300}
>
<section>
<button className="button" onClick={this.fadeImage.bind(this)}>Click!</button>
<img src={this.state.images[this.state.currentImage]}/></section>
</CSSTransitionGroup>
</div>
);
}
}
render(<FadeImage />, document.querySelector('#app'))
http://bl.ocks.org/piotrf/1b7b14b0c2bfbfe1f1ef this is almost exactly what you want. You can just get rid of the parts you dont need and change the buttons to the images you want

Resources